Merge remote-tracking branch 'github/master'
This commit is contained in:
commit
66ebf8d83f
183 changed files with 3804 additions and 771 deletions
2
Procfile
2
Procfile
|
@ -1,2 +1,2 @@
|
|||
web: bundle exec unicorn_rails -p $PORT
|
||||
worker: bundle exec sidekiq -q post_receive,mailer,system_hook,common,default
|
||||
worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default
|
||||
|
|
BIN
app/assets/images/onion_skin_sprites.gif
Normal file
BIN
app/assets/images/onion_skin_sprites.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
app/assets/images/swipemode_sprites.gif
Normal file
BIN
app/assets/images/swipemode_sprites.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
7
app/assets/javascripts/commit/file.js.coffee
Normal file
7
app/assets/javascripts/commit/file.js.coffee
Normal file
|
@ -0,0 +1,7 @@
|
|||
class CommitFile
|
||||
|
||||
constructor: (file) ->
|
||||
if $('.image', file).length
|
||||
new ImageFile(file)
|
||||
|
||||
this.CommitFile = CommitFile
|
128
app/assets/javascripts/commit/image-file.js.coffee
Normal file
128
app/assets/javascripts/commit/image-file.js.coffee
Normal file
|
@ -0,0 +1,128 @@
|
|||
class ImageFile
|
||||
|
||||
# Width where images must fits in, for 2-up this gets divided by 2
|
||||
@availWidth = 900
|
||||
@viewModes = ['two-up', 'swipe']
|
||||
|
||||
constructor: (@file) ->
|
||||
# Determine if old and new file has same dimensions, if not show 'two-up' view
|
||||
this.requestImageInfo $('.two-up.view .frame.deleted img', @file), (deletedWidth, deletedHeight) =>
|
||||
this.requestImageInfo $('.two-up.view .frame.added img', @file), (width, height) =>
|
||||
if width == deletedWidth && height == deletedHeight
|
||||
this.initViewModes()
|
||||
else
|
||||
this.initView('two-up')
|
||||
|
||||
initViewModes: ->
|
||||
viewMode = ImageFile.viewModes[0]
|
||||
|
||||
$('.view-modes', @file).removeClass 'hide'
|
||||
$('.view-modes-menu', @file).on 'click', 'li', (event) =>
|
||||
unless $(event.currentTarget).hasClass('active')
|
||||
this.activateViewMode(event.currentTarget.className)
|
||||
|
||||
this.activateViewMode(viewMode)
|
||||
|
||||
activateViewMode: (viewMode) ->
|
||||
$('.view-modes-menu li', @file)
|
||||
.removeClass('active')
|
||||
.filter(".#{viewMode}").addClass 'active'
|
||||
$(".view:visible:not(.#{viewMode})", @file).fadeOut 200, =>
|
||||
$(".view.#{viewMode}", @file).fadeIn(200)
|
||||
this.initView viewMode
|
||||
|
||||
initView: (viewMode) ->
|
||||
this.views[viewMode].call(this)
|
||||
|
||||
prepareFrames = (view) ->
|
||||
maxWidth = 0
|
||||
maxHeight = 0
|
||||
$('.frame', view).each (index, frame) =>
|
||||
width = $(frame).width()
|
||||
height = $(frame).height()
|
||||
maxWidth = if width > maxWidth then width else maxWidth
|
||||
maxHeight = if height > maxHeight then height else maxHeight
|
||||
.css
|
||||
width: maxWidth
|
||||
height: maxHeight
|
||||
|
||||
[maxWidth, maxHeight]
|
||||
|
||||
views:
|
||||
'two-up': ->
|
||||
$('.two-up.view .wrap', @file).each (index, wrap) =>
|
||||
$('img', wrap).each ->
|
||||
currentWidth = $(this).width()
|
||||
if currentWidth > ImageFile.availWidth / 2
|
||||
$(this).width ImageFile.availWidth / 2
|
||||
|
||||
this.requestImageInfo $('img', wrap), (width, height) ->
|
||||
$('.image-info .meta-width', wrap).text "#{width}px"
|
||||
$('.image-info .meta-height', wrap).text "#{height}px"
|
||||
$('.image-info', wrap).removeClass('hide')
|
||||
|
||||
'swipe': ->
|
||||
maxWidth = 0
|
||||
maxHeight = 0
|
||||
|
||||
$('.swipe.view', @file).each (index, view) =>
|
||||
|
||||
[maxWidth, maxHeight] = prepareFrames(view)
|
||||
|
||||
$('.swipe-frame', view).css
|
||||
width: maxWidth + 16
|
||||
height: maxHeight + 28
|
||||
|
||||
$('.swipe-wrap', view).css
|
||||
width: maxWidth + 1
|
||||
height: maxHeight + 2
|
||||
|
||||
$('.swipe-bar', view).css
|
||||
left: 0
|
||||
.draggable
|
||||
axis: 'x'
|
||||
containment: 'parent'
|
||||
drag: (event) ->
|
||||
$('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left
|
||||
stop: (event) ->
|
||||
$('.swipe-wrap', view).width (maxWidth + 1) - $(this).position().left
|
||||
|
||||
'onion-skin': ->
|
||||
maxWidth = 0
|
||||
maxHeight = 0
|
||||
|
||||
dragTrackWidth = $('.drag-track', @file).width() - $('.dragger', @file).width()
|
||||
|
||||
$('.onion-skin.view', @file).each (index, view) =>
|
||||
|
||||
[maxWidth, maxHeight] = prepareFrames(view)
|
||||
|
||||
$('.onion-skin-frame', view).css
|
||||
width: maxWidth + 16
|
||||
height: maxHeight + 28
|
||||
|
||||
$('.swipe-wrap', view).css
|
||||
width: maxWidth + 1
|
||||
height: maxHeight + 2
|
||||
|
||||
$('.dragger', view).css
|
||||
left: dragTrackWidth
|
||||
.draggable
|
||||
axis: 'x'
|
||||
containment: 'parent'
|
||||
drag: (event) ->
|
||||
$('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth)
|
||||
stop: (event) ->
|
||||
$('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth)
|
||||
|
||||
|
||||
|
||||
requestImageInfo: (img, callback) ->
|
||||
domImg = img.get(0)
|
||||
if domImg.complete
|
||||
callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
|
||||
else
|
||||
img.on 'load', =>
|
||||
callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
|
||||
|
||||
this.ImageFile = ImageFile
|
|
@ -1,59 +0,0 @@
|
|||
var CommitsList = {
|
||||
ref:null,
|
||||
limit:0,
|
||||
offset:0,
|
||||
disable:false,
|
||||
|
||||
init:
|
||||
function(ref, limit) {
|
||||
$(".day-commits-table li.commit").live('click', function(e){
|
||||
if(e.target.nodeName != "A") {
|
||||
location.href = $(this).attr("url");
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
this.ref=ref;
|
||||
this.limit=limit;
|
||||
this.offset=limit;
|
||||
this.initLoadMore();
|
||||
$('.loading').show();
|
||||
},
|
||||
|
||||
getOld:
|
||||
function() {
|
||||
$('.loading').show();
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: location.href,
|
||||
data: "limit=" + this.limit + "&offset=" + this.offset + "&ref=" + this.ref,
|
||||
complete: function(){ $('.loading').hide()},
|
||||
dataType: "script"});
|
||||
},
|
||||
|
||||
append:
|
||||
function(count, html) {
|
||||
$("#commits_list").append(html);
|
||||
if(count > 0) {
|
||||
this.offset += count;
|
||||
} else {
|
||||
this.disable = true;
|
||||
}
|
||||
},
|
||||
|
||||
initLoadMore:
|
||||
function() {
|
||||
$(document).endlessScroll({
|
||||
bottomPixels: 400,
|
||||
fireDelay: 1000,
|
||||
fireOnce:true,
|
||||
ceaseFire: function() {
|
||||
return CommitsList.disable;
|
||||
},
|
||||
callback: function(i) {
|
||||
CommitsList.getOld();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
54
app/assets/javascripts/commits.js.coffee
Normal file
54
app/assets/javascripts/commits.js.coffee
Normal file
|
@ -0,0 +1,54 @@
|
|||
class CommitsList
|
||||
@data =
|
||||
ref: null
|
||||
limit: 0
|
||||
offset: 0
|
||||
@disable = false
|
||||
|
||||
@showProgress: ->
|
||||
$('.loading').show()
|
||||
|
||||
@hideProgress: ->
|
||||
$('.loading').hide()
|
||||
|
||||
@init: (ref, limit) ->
|
||||
$(".day-commits-table li.commit").live 'click', (event) ->
|
||||
if event.target.nodeName != "A"
|
||||
location.href = $(this).attr("url")
|
||||
e.stopPropagation()
|
||||
return false
|
||||
|
||||
@data.ref = ref
|
||||
@data.limit = limit
|
||||
@data.offset = limit
|
||||
|
||||
this.initLoadMore()
|
||||
this.showProgress();
|
||||
|
||||
@getOld: ->
|
||||
this.showProgress()
|
||||
$.ajax
|
||||
type: "GET"
|
||||
url: location.href
|
||||
data: @data
|
||||
complete: this.hideProgress
|
||||
dataType: "script"
|
||||
|
||||
@append: (count, html) ->
|
||||
$("#commits-list").append(html)
|
||||
if count > 0
|
||||
@data.offset += count
|
||||
else
|
||||
@disable = true
|
||||
|
||||
@initLoadMore: ->
|
||||
$(document).endlessScroll
|
||||
bottomPixels: 400
|
||||
fireDelay: 1000
|
||||
fireOnce: true
|
||||
ceaseFire: =>
|
||||
@disable
|
||||
callback: =>
|
||||
this.getOld()
|
||||
|
||||
this.CommitsList = CommitsList
|
|
@ -4,11 +4,11 @@ window.dashboardPage = ->
|
|||
event.preventDefault()
|
||||
toggleFilter $(this)
|
||||
reloadActivities()
|
||||
|
||||
|
||||
reloadActivities = ->
|
||||
$(".content_list").html ''
|
||||
Pager.init 20, true
|
||||
|
||||
|
||||
toggleFilter = (sender) ->
|
||||
sender.parent().toggleClass "inactive"
|
||||
event_filters = $.cookie("event_filter")
|
||||
|
@ -17,11 +17,11 @@ toggleFilter = (sender) ->
|
|||
event_filters = event_filters.split(",")
|
||||
else
|
||||
event_filters = new Array()
|
||||
|
||||
|
||||
index = event_filters.indexOf(filter)
|
||||
if index is -1
|
||||
event_filters.push filter
|
||||
else
|
||||
event_filters.splice index, 1
|
||||
|
||||
|
||||
$.cookie "event_filter", event_filters.join(",")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#
|
||||
# * Filter merge requests
|
||||
#
|
||||
#
|
||||
@merge_requestsPage = ->
|
||||
$('#assignee_id').chosen()
|
||||
$('#milestone_id').chosen()
|
||||
|
@ -8,16 +8,16 @@
|
|||
$(this).closest('form').submit()
|
||||
|
||||
class MergeRequest
|
||||
|
||||
|
||||
constructor: (@opts) ->
|
||||
this.$el = $('.merge-request')
|
||||
@diffs_loaded = false
|
||||
@commits_loaded = false
|
||||
|
||||
|
||||
this.activateTab(@opts.action)
|
||||
|
||||
|
||||
this.bindEvents()
|
||||
|
||||
|
||||
this.initMergeWidget()
|
||||
this.$('.show-all-commits').on 'click', =>
|
||||
this.showAllCommits()
|
||||
|
@ -28,7 +28,7 @@ class MergeRequest
|
|||
|
||||
initMergeWidget: ->
|
||||
this.showState( @opts.current_state )
|
||||
|
||||
|
||||
if this.$('.automerge_widget').length and @opts.check_enable
|
||||
$.get @opts.url_to_automerge_check, (data) =>
|
||||
this.showState( data.state )
|
||||
|
@ -42,12 +42,12 @@ class MergeRequest
|
|||
bindEvents: ->
|
||||
this.$('.nav-tabs').on 'click', 'a', (event) =>
|
||||
a = $(event.currentTarget)
|
||||
|
||||
|
||||
href = a.attr('href')
|
||||
History.replaceState {path: href}, document.title, href
|
||||
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
|
||||
this.$('.nav-tabs').on 'click', 'li', (event) =>
|
||||
this.activateTab($(event.currentTarget).data('action'))
|
||||
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
var Pager = {
|
||||
limit:0,
|
||||
offset:0,
|
||||
disable:false,
|
||||
|
||||
init:
|
||||
function(limit, preload) {
|
||||
this.limit=limit;
|
||||
|
||||
if(preload) {
|
||||
this.offset = 0;
|
||||
this.getOld();
|
||||
} else {
|
||||
this.offset = limit;
|
||||
}
|
||||
|
||||
this.initLoadMore();
|
||||
},
|
||||
|
||||
getOld:
|
||||
function() {
|
||||
$('.loading').show();
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: location.href,
|
||||
data: "limit=" + this.limit + "&offset=" + this.offset,
|
||||
complete: function(){ $('.loading').hide()},
|
||||
dataType: "script"});
|
||||
},
|
||||
|
||||
append:
|
||||
function(count, html) {
|
||||
$(".content_list").append(html);
|
||||
if(count > 0) {
|
||||
this.offset += count;
|
||||
} else {
|
||||
this.disable = true;
|
||||
}
|
||||
},
|
||||
|
||||
initLoadMore:
|
||||
function() {
|
||||
$(document).endlessScroll({
|
||||
bottomPixels: 400,
|
||||
fireDelay: 1000,
|
||||
fireOnce:true,
|
||||
ceaseFire: function() {
|
||||
return Pager.disable;
|
||||
},
|
||||
callback: function(i) {
|
||||
$('.loading').show();
|
||||
Pager.getOld();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
42
app/assets/javascripts/pager.js.coffee
Normal file
42
app/assets/javascripts/pager.js.coffee
Normal file
|
@ -0,0 +1,42 @@
|
|||
@Pager =
|
||||
limit: 0
|
||||
offset: 0
|
||||
disable: false
|
||||
init: (limit, preload) ->
|
||||
@limit = limit
|
||||
if preload
|
||||
@offset = 0
|
||||
@getOld()
|
||||
else
|
||||
@offset = limit
|
||||
@initLoadMore()
|
||||
|
||||
getOld: ->
|
||||
$(".loading").show()
|
||||
$.ajax
|
||||
type: "GET"
|
||||
url: location.href
|
||||
data: "limit=" + @limit + "&offset=" + @offset
|
||||
complete: ->
|
||||
$(".loading").hide()
|
||||
|
||||
dataType: "script"
|
||||
|
||||
append: (count, html) ->
|
||||
$(".content_list").append html
|
||||
if count > 0
|
||||
@offset += count
|
||||
else
|
||||
@disable = true
|
||||
|
||||
initLoadMore: ->
|
||||
$(document).endlessScroll
|
||||
bottomPixels: 400
|
||||
fireDelay: 1000
|
||||
fireOnce: true
|
||||
ceaseFire: ->
|
||||
Pager.disable
|
||||
|
||||
callback: (i) ->
|
||||
$(".loading").show()
|
||||
Pager.getOld()
|
|
@ -1,5 +1,5 @@
|
|||
html {
|
||||
overflow-y: scroll;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
/** LAYOUT **/
|
||||
|
@ -277,8 +277,20 @@ p.time {
|
|||
}
|
||||
}
|
||||
|
||||
.search-holder {
|
||||
label, input {
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
label {
|
||||
line-height: 30px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.highlight_word {
|
||||
background: #EEDC94;
|
||||
border-bottom: 2px solid #F90;
|
||||
}
|
||||
|
||||
.status_info {
|
||||
|
@ -326,7 +338,7 @@ li.note {
|
|||
li {
|
||||
border-bottom:none !important;
|
||||
}
|
||||
.file {
|
||||
.attachment {
|
||||
padding-left: 20px;
|
||||
background:url("icon-attachment.png") no-repeat left center;
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@
|
|||
pre {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
|
||||
font-family: $monospace_font;
|
||||
font-size: 12px !important;
|
||||
line-height: 16px !important;
|
||||
margin: 0;
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
}
|
||||
|
||||
/** Typo **/
|
||||
$monospace: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;
|
||||
$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;
|
||||
|
|
|
@ -21,7 +21,7 @@ h6 {
|
|||
|
||||
/** CODE **/
|
||||
pre {
|
||||
font-family:'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
|
||||
font-family: $monospace_font;
|
||||
|
||||
&.dark {
|
||||
background: #333;
|
||||
|
@ -79,7 +79,7 @@ a:focus {
|
|||
}
|
||||
|
||||
.monospace {
|
||||
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
|
||||
font-family: $monospace_font;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
/** Colors **/
|
||||
/**
|
||||
* General Colors
|
||||
*/
|
||||
$primary_color: #2FA0BB;
|
||||
$link_color: #3A89A3;
|
||||
$style_color: #474D57;
|
||||
$hover: #D9EDF7;
|
||||
|
||||
/**
|
||||
* Commit Diff Colors
|
||||
*/
|
||||
$added: #63c363;
|
||||
$deleted: #f77;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
/**
|
||||
*
|
||||
* COMMIT SHOw
|
||||
*
|
||||
* Commit file
|
||||
*/
|
||||
.commit-committer-link,
|
||||
.commit-author-link {
|
||||
|
@ -12,11 +10,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.diff_file {
|
||||
.file {
|
||||
border: 1px solid #CCC;
|
||||
margin-bottom: 1em;
|
||||
|
||||
.diff_file_header {
|
||||
.header {
|
||||
@extend .clearfix;
|
||||
padding: 5px 5px 5px 10px;
|
||||
color: #555;
|
||||
|
@ -28,32 +26,35 @@
|
|||
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
|
||||
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
|
||||
|
||||
a{
|
||||
color: $style_color;
|
||||
}
|
||||
|
||||
> span {
|
||||
font-family: $monospace;
|
||||
font-family: $monospace_font;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
a.view-commit{
|
||||
a.view-file{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.commit-short-id{
|
||||
font-family: $monospace;
|
||||
font-family: $monospace_font;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.file-mode{
|
||||
font-family: $monospace;
|
||||
font-family: $monospace_font;
|
||||
}
|
||||
}
|
||||
.diff_file_content {
|
||||
.content {
|
||||
overflow: auto;
|
||||
overflow-y: hidden;
|
||||
background: #fff;
|
||||
background: #FFF;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
font-family: $monospace;
|
||||
.old{
|
||||
span.idiff{
|
||||
background-color: #FAA;
|
||||
|
@ -66,114 +67,274 @@
|
|||
}
|
||||
|
||||
table {
|
||||
font-family: $monospace_font;
|
||||
border: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
td {
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.diff_file_content_image {
|
||||
background: #eee;
|
||||
text-align: center;
|
||||
.image {
|
||||
display: inline-block;
|
||||
margin: 50px;
|
||||
max-width: 400px;
|
||||
|
||||
img{
|
||||
background: url('trans_bg.gif');
|
||||
}
|
||||
|
||||
&.diff_removed {
|
||||
img{
|
||||
border: 1px solid #C00;
|
||||
}
|
||||
}
|
||||
|
||||
&.diff_added {
|
||||
img{
|
||||
border: 1px solid #0C0;
|
||||
}
|
||||
}
|
||||
|
||||
.image-info{
|
||||
margin: 5px 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.img_compared {
|
||||
.image {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.diff_file_content{
|
||||
table {
|
||||
border: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
tr {
|
||||
td {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.new_line,
|
||||
.old_line,
|
||||
.notes_line {
|
||||
margin:0px;
|
||||
padding:0px;
|
||||
border:none;
|
||||
background:#EEE;
|
||||
color:#666;
|
||||
padding: 0px 5px;
|
||||
border-right: 1px solid #ccc;
|
||||
text-align: right;
|
||||
min-width: 35px;
|
||||
max-width: 35px;
|
||||
width: 35px;
|
||||
moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
a {
|
||||
float: left;
|
||||
width: 35px;
|
||||
font-weight: normal;
|
||||
.old_line, .new_line {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
border: none;
|
||||
background: #EEE;
|
||||
color: #666;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
padding: 0px 5px;
|
||||
border-right: 1px solid #ccc;
|
||||
text-align: right;
|
||||
min-width: 35px;
|
||||
max-width: 35px;
|
||||
width: 35px;
|
||||
@include user-select(none);
|
||||
a {
|
||||
float: left;
|
||||
width: 35px;
|
||||
font-weight: normal;
|
||||
color: #666;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
.line_content {
|
||||
white-space: pre;
|
||||
height: 14px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
border: none;
|
||||
&.new {
|
||||
background: #CFD;
|
||||
}
|
||||
&.old {
|
||||
background: #FDD;
|
||||
}
|
||||
&.matched {
|
||||
color: #ccc;
|
||||
background: #fafafa;
|
||||
}
|
||||
}
|
||||
}
|
||||
.line_content {
|
||||
white-space: pre;
|
||||
height: 14px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
border: none;
|
||||
&.new {
|
||||
background: #CFD;
|
||||
.image {
|
||||
background: #ddd;
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
.wrap{
|
||||
display: inline-block;
|
||||
}
|
||||
&.old {
|
||||
background: #FDD;
|
||||
|
||||
.frame {
|
||||
display: inline-block;
|
||||
background-color: #fff;
|
||||
line-height: 0;
|
||||
img{
|
||||
border: 1px solid #FFF;
|
||||
background: url('trans_bg.gif');
|
||||
}
|
||||
&.deleted {
|
||||
border: 1px solid $deleted;
|
||||
}
|
||||
|
||||
&.added {
|
||||
border: 1px solid $added;
|
||||
}
|
||||
}
|
||||
&.matched {
|
||||
color: #ccc;
|
||||
background: #fafafa;
|
||||
.image-info{
|
||||
font-size: 12px;
|
||||
margin: 5px 0 0 0;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.view.swipe{
|
||||
position: relative;
|
||||
|
||||
.swipe-frame{
|
||||
display: block;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
}
|
||||
.swipe-wrap{
|
||||
overflow: hidden;
|
||||
border-left: 1px solid #999;
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 13px;
|
||||
right: 7px;
|
||||
}
|
||||
.frame{
|
||||
top: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
&.deleted{
|
||||
margin: 0;
|
||||
display: block;
|
||||
top: 13px;
|
||||
right: 7px;
|
||||
}
|
||||
}
|
||||
.swipe-bar{
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 15px;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
&:hover{
|
||||
.top-handle{
|
||||
background-position: -15px 3px;
|
||||
}
|
||||
.bottom-handle{
|
||||
background-position: -15px -11px;
|
||||
}
|
||||
};
|
||||
.top-handle{
|
||||
display: block;
|
||||
height: 14px;
|
||||
width: 15px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
background: url('swipemode_sprites.gif') 0 3px no-repeat;
|
||||
}
|
||||
.bottom-handle{
|
||||
display: block;
|
||||
height: 14px;
|
||||
width: 15px;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
background: url('swipemode_sprites.gif') 0 -11px no-repeat;
|
||||
}
|
||||
}
|
||||
} //.view.swipe
|
||||
.view.onion-skin{
|
||||
.onion-skin-frame{
|
||||
display: block;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
}
|
||||
.frame.added, .frame.deleted {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
.controls{
|
||||
display: block;
|
||||
height: 14px;
|
||||
width: 300px;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 50%;
|
||||
margin-left: -150px;
|
||||
|
||||
.drag-track{
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
height: 10px;
|
||||
width: 276px;
|
||||
background: url('onion_skin_sprites.gif') -4px -20px repeat-x;
|
||||
}
|
||||
|
||||
.dragger {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
background: url('onion_skin_sprites.gif') 0px -34px repeat-x;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.transparent {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 0px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
background: url('onion_skin_sprites.gif') -2px 0px no-repeat;
|
||||
}
|
||||
|
||||
.opaque {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 0px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
background: url('onion_skin_sprites.gif') -2px -10px no-repeat;
|
||||
}
|
||||
}
|
||||
} //.view.onion-skin
|
||||
}
|
||||
.view-modes{
|
||||
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
|
||||
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
|
||||
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
|
||||
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
|
||||
|
||||
ul, li{
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
li{
|
||||
color: grey;
|
||||
border-left: 1px solid #c1c1c1;
|
||||
padding: 0 12px 0 16px;
|
||||
cursor: pointer;
|
||||
&:first-child{
|
||||
border-left: none;
|
||||
}
|
||||
&:hover{
|
||||
text-decoration: underline;
|
||||
}
|
||||
&.active{
|
||||
&:hover{
|
||||
text-decoration: none;
|
||||
}
|
||||
cursor: default;
|
||||
color: #333;
|
||||
}
|
||||
&.disabled{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** COMMIT BLOCK **/
|
||||
.commit-title{display: block;}
|
||||
.commit-title{margin-bottom: 10px}
|
||||
.commit-author, .commit-committer{display: block;color: #999; font-weight: normal; font-style: italic;}
|
||||
.commit-author strong, .commit-committer strong{font-weight: bold; font-style: normal;}
|
||||
.commit-title{
|
||||
display: block;
|
||||
}
|
||||
.commit-title{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.commit-author, .commit-committer{
|
||||
display: block;
|
||||
color: #999;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
.commit-author strong, .commit-committer strong{
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
/** COMMIT ROW **/
|
||||
/**
|
||||
* COMMIT ROW
|
||||
*/
|
||||
.commit {
|
||||
.browse_code_link_holder {
|
||||
@extend .span2;
|
||||
|
@ -199,11 +360,10 @@
|
|||
float: left;
|
||||
@extend .lined;
|
||||
min-width: 65px;
|
||||
font-family: $monospace;
|
||||
font-family: $monospace_font;
|
||||
}
|
||||
}
|
||||
|
||||
.diff_file_header a,
|
||||
.file-stats a {
|
||||
color: $style_color;
|
||||
}
|
||||
|
@ -237,7 +397,7 @@
|
|||
font-size: 13px;
|
||||
background: #474D57;
|
||||
color: #fff;
|
||||
font-family: $monospace;
|
||||
font-family: $monospace_font;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ li.merge_request {
|
|||
font-size: 14px;
|
||||
background: #474D57;
|
||||
color: #fff;
|
||||
font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono','lucida console',monospace;
|
||||
font-family: $monospace_font;
|
||||
}
|
||||
|
||||
.mr_source_commit,
|
||||
|
|
|
@ -40,13 +40,13 @@ ul.notes {
|
|||
.discussion-body {
|
||||
margin-left: 50px;
|
||||
|
||||
.diff_file,
|
||||
.file,
|
||||
.discussion-hidden,
|
||||
.notes {
|
||||
@extend .borders;
|
||||
background-color: #F9F9F9;
|
||||
}
|
||||
.diff_file .notes {
|
||||
.file .notes {
|
||||
/* reset */
|
||||
background: inherit;
|
||||
border: none;
|
||||
|
@ -109,7 +109,7 @@ ul.notes {
|
|||
}
|
||||
}
|
||||
|
||||
.diff_file .notes_holder {
|
||||
.file .notes_holder {
|
||||
font-family: $sansFontFamily;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
|
@ -134,8 +134,6 @@ ul.notes {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Actions for Discussions/Notes
|
||||
*/
|
||||
|
@ -171,7 +169,7 @@ ul.notes {
|
|||
}
|
||||
}
|
||||
}
|
||||
.diff_file .note .note-actions {
|
||||
.file .note .note-actions {
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
@ -182,7 +180,7 @@ ul.notes {
|
|||
* Line note button on the side of diffs
|
||||
*/
|
||||
|
||||
.diff_file tr.line_holder {
|
||||
.file tr.line_holder {
|
||||
.add-diff-note {
|
||||
background: url("diff_note_add.png") no-repeat left 0;
|
||||
height: 22px;
|
||||
|
@ -212,8 +210,6 @@ ul.notes {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Note Form
|
||||
*/
|
||||
|
@ -222,7 +218,12 @@ ul.notes {
|
|||
.reply-btn {
|
||||
@extend .save-btn;
|
||||
}
|
||||
.diff_file,
|
||||
.file .content tr.line_holder:hover > td { background: $hover !important; }
|
||||
.file .content tr.line_holder:hover > td .line_note_link {
|
||||
opacity: 1.0;
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
.file,
|
||||
.discussion {
|
||||
.new_note {
|
||||
margin: 8px 5px 8px 0;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
.side {
|
||||
@extend .right;
|
||||
|
||||
.groups_box,
|
||||
.projects_box {
|
||||
> .title {
|
||||
padding: 2px 15px;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Provides a base class for Admin controllers to subclass
|
||||
#
|
||||
# Automatically sets the layout and ensures an administrator is logged in
|
||||
class AdminController < ApplicationController
|
||||
class Admin::ApplicationController < ApplicationController
|
||||
layout 'admin'
|
||||
before_filter :authenticate_admin!
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
class Admin::DashboardController < AdminController
|
||||
class Admin::DashboardController < Admin::ApplicationController
|
||||
def index
|
||||
@projects = Project.order("created_at DESC").limit(10)
|
||||
@users = User.order("created_at DESC").limit(10)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class Admin::GroupsController < AdminController
|
||||
class Admin::GroupsController < Admin::ApplicationController
|
||||
before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update]
|
||||
|
||||
def index
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class Admin::HooksController < AdminController
|
||||
class Admin::HooksController < Admin::ApplicationController
|
||||
def index
|
||||
@hooks = SystemHook.all
|
||||
@hook = SystemHook.new
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
class Admin::LogsController < AdminController
|
||||
class Admin::LogsController < Admin::ApplicationController
|
||||
end
|
||||
|
|
11
app/controllers/admin/projects/application_controller.rb
Normal file
11
app/controllers/admin/projects/application_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Provides a base class for Admin controllers to subclass
|
||||
#
|
||||
# Automatically sets the layout and ensures an administrator is logged in
|
||||
class Admin::Projects::ApplicationController < Admin::ApplicationController
|
||||
|
||||
protected
|
||||
|
||||
def project
|
||||
@project ||= Project.find_with_namespace(params[:project_id])
|
||||
end
|
||||
end
|
32
app/controllers/admin/projects/members_controller.rb
Normal file
32
app/controllers/admin/projects/members_controller.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
class Admin::Projects::MembersController < Admin::Projects::ApplicationController
|
||||
def edit
|
||||
@member = team_member
|
||||
@project = project
|
||||
@team_member_relation = team_member_relation
|
||||
end
|
||||
|
||||
def update
|
||||
if team_member_relation.update_attributes(params[:team_member])
|
||||
redirect_to [:admin, project], notice: 'Project Access was successfully updated.'
|
||||
else
|
||||
render action: "edit"
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
team_member_relation.destroy
|
||||
|
||||
redirect_to :back
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def team_member
|
||||
@member ||= project.users.find_by_username(params[:id])
|
||||
end
|
||||
|
||||
def team_member_relation
|
||||
team_member.users_projects.find_by_project_id(project)
|
||||
end
|
||||
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
class Admin::ProjectsController < AdminController
|
||||
class Admin::ProjectsController < Admin::ApplicationController
|
||||
before_filter :project, only: [:edit, :show, :update, :destroy, :team_update]
|
||||
|
||||
def index
|
||||
|
@ -29,7 +29,9 @@ class Admin::ProjectsController < AdminController
|
|||
end
|
||||
|
||||
def update
|
||||
status = Projects::UpdateContext.new(project, current_user, params).execute(:admin)
|
||||
project.creator = current_user unless project.creator
|
||||
|
||||
status = ::Projects::UpdateContext.new(project, current_user, params).execute(:admin)
|
||||
|
||||
if status
|
||||
redirect_to [:admin, @project], notice: 'Project was successfully updated.'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class Admin::ResqueController < AdminController
|
||||
class Admin::ResqueController < Admin::ApplicationController
|
||||
def show
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
class Admin::TeamMembersController < AdminController
|
||||
def edit
|
||||
@admin_team_member = UsersProject.find(params[:id])
|
||||
end
|
||||
|
||||
def update
|
||||
@admin_team_member = UsersProject.find(params[:id])
|
||||
|
||||
if @admin_team_member.update_attributes(params[:team_member])
|
||||
redirect_to [:admin, @admin_team_member.project], notice: 'Project Access was successfully updated.'
|
||||
else
|
||||
render action: "edit"
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@admin_team_member = UsersProject.find(params[:id])
|
||||
@admin_team_member.destroy
|
||||
|
||||
redirect_to :back
|
||||
end
|
||||
end
|
11
app/controllers/admin/teams/application_controller.rb
Normal file
11
app/controllers/admin/teams/application_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Provides a base class for Admin controllers to subclass
|
||||
#
|
||||
# Automatically sets the layout and ensures an administrator is logged in
|
||||
class Admin::Teams::ApplicationController < Admin::ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def user_team
|
||||
@team = UserTeam.find_by_path(params[:team_id])
|
||||
end
|
||||
end
|
41
app/controllers/admin/teams/members_controller.rb
Normal file
41
app/controllers/admin/teams/members_controller.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
class Admin::Teams::MembersController < Admin::Teams::ApplicationController
|
||||
def new
|
||||
@users = User.potential_team_members(user_team)
|
||||
@users = UserDecorator.decorate @users
|
||||
end
|
||||
|
||||
def create
|
||||
unless params[:user_ids].blank?
|
||||
user_ids = params[:user_ids]
|
||||
access = params[:default_project_access]
|
||||
is_admin = params[:group_admin]
|
||||
user_team.add_members(user_ids, access, is_admin)
|
||||
end
|
||||
|
||||
redirect_to admin_team_path(user_team), notice: 'Members was successfully added into Team of users.'
|
||||
end
|
||||
|
||||
def edit
|
||||
team_member
|
||||
end
|
||||
|
||||
def update
|
||||
options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]}
|
||||
if user_team.update_membership(team_member, options)
|
||||
redirect_to admin_team_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users."
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
user_team.remove_member(team_member)
|
||||
redirect_to admin_team_path(user_team), notice: "Member #{team_member.name} was successfully removed from Team of users."
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def team_member
|
||||
@member ||= user_team.members.find_by_username(params[:id])
|
||||
end
|
||||
end
|
41
app/controllers/admin/teams/projects_controller.rb
Normal file
41
app/controllers/admin/teams/projects_controller.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
class Admin::Teams::ProjectsController < Admin::Teams::ApplicationController
|
||||
def new
|
||||
@projects = Project.scoped
|
||||
@projects = @projects.without_team(user_team) if user_team.projects.any?
|
||||
#@projects.reject!(&:empty_repo?)
|
||||
end
|
||||
|
||||
def create
|
||||
unless params[:project_ids].blank?
|
||||
project_ids = params[:project_ids]
|
||||
access = params[:greatest_project_access]
|
||||
user_team.assign_to_projects(project_ids, access)
|
||||
end
|
||||
|
||||
redirect_to admin_team_path(user_team), notice: 'Team of users was successfully assgned to projects.'
|
||||
end
|
||||
|
||||
def edit
|
||||
team_project
|
||||
end
|
||||
|
||||
def update
|
||||
if user_team.update_project_access(team_project, params[:greatest_project_access])
|
||||
redirect_to admin_team_path(user_team), notice: 'Access was successfully updated.'
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
user_team.resign_from_project(team_project)
|
||||
redirect_to admin_team_path(user_team), notice: 'Team of users was successfully reassigned from project.'
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def team_project
|
||||
@project ||= user_team.projects.find_with_namespace(params[:id])
|
||||
end
|
||||
|
||||
end
|
59
app/controllers/admin/teams_controller.rb
Normal file
59
app/controllers/admin/teams_controller.rb
Normal file
|
@ -0,0 +1,59 @@
|
|||
class Admin::TeamsController < Admin::ApplicationController
|
||||
def index
|
||||
@teams = UserTeam.order('name ASC')
|
||||
@teams = @teams.search(params[:name]) if params[:name].present?
|
||||
@teams = @teams.page(params[:page]).per(20)
|
||||
end
|
||||
|
||||
def show
|
||||
user_team
|
||||
end
|
||||
|
||||
def new
|
||||
@team = UserTeam.new
|
||||
end
|
||||
|
||||
def edit
|
||||
user_team
|
||||
end
|
||||
|
||||
def create
|
||||
@team = UserTeam.new(params[:user_team])
|
||||
@team.path = @team.name.dup.parameterize if @team.name
|
||||
@team.owner = current_user
|
||||
|
||||
if @team.save
|
||||
redirect_to admin_team_path(@team), notice: 'Team of users was successfully created.'
|
||||
else
|
||||
render action: "new"
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
user_team_params = params[:user_team].dup
|
||||
owner_id = user_team_params.delete(:owner_id)
|
||||
|
||||
if owner_id
|
||||
user_team.owner = User.find(owner_id)
|
||||
end
|
||||
|
||||
if user_team.update_attributes(user_team_params)
|
||||
redirect_to admin_team_path(user_team), notice: 'Team of users was successfully updated.'
|
||||
else
|
||||
render action: "edit"
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
user_team.destroy
|
||||
|
||||
redirect_to admin_teams_path, notice: 'Team of users was successfully deleted.'
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def user_team
|
||||
@team ||= UserTeam.find_by_path(params[:id])
|
||||
end
|
||||
|
||||
end
|
|
@ -1,4 +1,6 @@
|
|||
class Admin::UsersController < AdminController
|
||||
class Admin::UsersController < Admin::ApplicationController
|
||||
before_filter :admin_user, only: [:show, :edit, :update, :destroy]
|
||||
|
||||
def index
|
||||
@admin_users = User.scoped
|
||||
@admin_users = @admin_users.filter(params[:filter])
|
||||
|
@ -7,25 +9,18 @@ class Admin::UsersController < AdminController
|
|||
end
|
||||
|
||||
def show
|
||||
@admin_user = User.find(params[:id])
|
||||
|
||||
@projects = if @admin_user.authorized_projects.empty?
|
||||
Project
|
||||
else
|
||||
Project.without_user(@admin_user)
|
||||
end.all
|
||||
@projects = Project.scoped
|
||||
@projects = @projects.without_user(admin_user) if admin_user.authorized_projects.present?
|
||||
end
|
||||
|
||||
def team_update
|
||||
@admin_user = User.find(params[:id])
|
||||
|
||||
UsersProject.add_users_into_projects(
|
||||
params[:project_ids],
|
||||
[@admin_user.id],
|
||||
[admin_user.id],
|
||||
params[:project_access]
|
||||
)
|
||||
|
||||
redirect_to [:admin, @admin_user], notice: 'Teams were successfully updated.'
|
||||
redirect_to [:admin, admin_user], notice: 'Teams were successfully updated.'
|
||||
end
|
||||
|
||||
|
||||
|
@ -34,13 +29,11 @@ class Admin::UsersController < AdminController
|
|||
end
|
||||
|
||||
def edit
|
||||
@admin_user = User.find(params[:id])
|
||||
admin_user
|
||||
end
|
||||
|
||||
def block
|
||||
@admin_user = User.find(params[:id])
|
||||
|
||||
if @admin_user.block
|
||||
if admin_user.block
|
||||
redirect_to :back, alert: "Successfully blocked"
|
||||
else
|
||||
redirect_to :back, alert: "Error occured. User was not blocked"
|
||||
|
@ -48,9 +41,7 @@ class Admin::UsersController < AdminController
|
|||
end
|
||||
|
||||
def unblock
|
||||
@admin_user = User.find(params[:id])
|
||||
|
||||
if @admin_user.update_attribute(:blocked, false)
|
||||
if admin_user.update_attribute(:blocked, false)
|
||||
redirect_to :back, alert: "Successfully unblocked"
|
||||
else
|
||||
redirect_to :back, alert: "Error occured. User was not unblocked"
|
||||
|
@ -82,30 +73,34 @@ class Admin::UsersController < AdminController
|
|||
params[:user].delete(:password_confirmation)
|
||||
end
|
||||
|
||||
@admin_user = User.find(params[:id])
|
||||
@admin_user.admin = (admin && admin.to_i > 0)
|
||||
admin_user.admin = (admin && admin.to_i > 0)
|
||||
|
||||
respond_to do |format|
|
||||
if @admin_user.update_attributes(params[:user], as: :admin)
|
||||
format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully updated.' }
|
||||
if admin_user.update_attributes(params[:user], as: :admin)
|
||||
format.html { redirect_to [:admin, admin_user], notice: 'User was successfully updated.' }
|
||||
format.json { head :ok }
|
||||
else
|
||||
format.html { render action: "edit" }
|
||||
format.json { render json: @admin_user.errors, status: :unprocessable_entity }
|
||||
format.json { render json: admin_user.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@admin_user = User.find(params[:id])
|
||||
if @admin_user.personal_projects.count > 0
|
||||
if admin_user.personal_projects.count > 0
|
||||
redirect_to admin_users_path, alert: "User is a project owner and can't be removed." and return
|
||||
end
|
||||
@admin_user.destroy
|
||||
admin_user.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to admin_users_url }
|
||||
format.html { redirect_to admin_users_path }
|
||||
format.json { head :ok }
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def admin_user
|
||||
@admin_user ||= User.find_by_username!(params[:id])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -94,6 +94,18 @@ class ApplicationController < ActionController::Base
|
|||
return access_denied! unless can?(current_user, :download_code, project)
|
||||
end
|
||||
|
||||
def authorize_create_team!
|
||||
return access_denied! unless can?(current_user, :create_team, nil)
|
||||
end
|
||||
|
||||
def authorize_manage_user_team!
|
||||
return access_denied! unless user_team.present? && can?(current_user, :manage_user_team, user_team)
|
||||
end
|
||||
|
||||
def authorize_admin_user_team!
|
||||
return access_denied! unless user_team.present? && can?(current_user, :admin_user_team, user_team)
|
||||
end
|
||||
|
||||
def access_denied!
|
||||
render "errors/access_denied", layout: "errors", status: 404
|
||||
end
|
||||
|
@ -135,4 +147,5 @@ class ApplicationController < ActionController::Base
|
|||
def dev_tools
|
||||
Rack::MiniProfiler.authorize_request
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,24 +1,15 @@
|
|||
class DashboardController < ApplicationController
|
||||
respond_to :html
|
||||
|
||||
before_filter :projects
|
||||
before_filter :event_filter, only: :index
|
||||
before_filter :load_projects
|
||||
before_filter :event_filter, only: :show
|
||||
|
||||
def index
|
||||
def show
|
||||
@groups = current_user.authorized_groups
|
||||
|
||||
@has_authorized_projects = @projects.count > 0
|
||||
|
||||
@projects = case params[:scope]
|
||||
when 'personal' then
|
||||
@projects.personal(current_user)
|
||||
when 'joined' then
|
||||
@projects.joined(current_user)
|
||||
else
|
||||
@projects
|
||||
end
|
||||
|
||||
@projects = @projects.page(params[:page]).per(30)
|
||||
@teams = current_user.authorized_teams
|
||||
@projects_count = @projects.count
|
||||
@projects = @projects.limit(20)
|
||||
|
||||
@events = Event.in_projects(current_user.authorized_projects.pluck(:id))
|
||||
@events = @event_filter.apply_filter(@events)
|
||||
|
@ -33,6 +24,19 @@ class DashboardController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def projects
|
||||
@projects = case params[:scope]
|
||||
when 'personal' then
|
||||
@projects.personal(current_user)
|
||||
when 'joined' then
|
||||
@projects.joined(current_user)
|
||||
else
|
||||
@projects
|
||||
end
|
||||
|
||||
@projects = @projects.page(params[:page]).per(30)
|
||||
end
|
||||
|
||||
# Get authored or assigned open merge requests
|
||||
def merge_requests
|
||||
@merge_requests = current_user.cared_merge_requests
|
||||
|
@ -55,7 +59,7 @@ class DashboardController < ApplicationController
|
|||
|
||||
protected
|
||||
|
||||
def projects
|
||||
def load_projects
|
||||
@projects = current_user.authorized_projects.sorted_by_activity
|
||||
end
|
||||
|
||||
|
|
|
@ -1,12 +1,31 @@
|
|||
class GroupsController < ApplicationController
|
||||
respond_to :html
|
||||
layout 'group'
|
||||
layout 'group', except: [:new, :create]
|
||||
|
||||
before_filter :group
|
||||
before_filter :projects
|
||||
before_filter :group, except: [:new, :create]
|
||||
|
||||
# Authorize
|
||||
before_filter :authorize_read_group!
|
||||
before_filter :authorize_read_group!, except: [:new, :create]
|
||||
before_filter :authorize_create_group!, only: [:new, :create]
|
||||
|
||||
# Load group projects
|
||||
before_filter :projects, except: [:new, :create]
|
||||
|
||||
def new
|
||||
@group = Group.new
|
||||
end
|
||||
|
||||
def create
|
||||
@group = Group.new(params[:group])
|
||||
@group.path = @group.name.dup.parameterize if @group.name
|
||||
@group.owner = current_user
|
||||
|
||||
if @group.save
|
||||
redirect_to @group, notice: 'Group was successfully created.'
|
||||
else
|
||||
render action: "new"
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
@events = Event.in_projects(project_ids).limit(20).offset(params[:offset] || 0)
|
||||
|
@ -85,4 +104,8 @@ class GroupsController < ApplicationController
|
|||
return render_404
|
||||
end
|
||||
end
|
||||
|
||||
def authorize_create_group!
|
||||
can?(current_user, :create_group, nil)
|
||||
end
|
||||
end
|
||||
|
|
11
app/controllers/projects/application_controller.rb
Normal file
11
app/controllers/projects/application_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
class Projects::ApplicationController < ApplicationController
|
||||
|
||||
before_filter :authorize_admin_team_member!
|
||||
|
||||
protected
|
||||
|
||||
def user_team
|
||||
@team ||= UserTeam.find_by_path(params[:id])
|
||||
end
|
||||
|
||||
end
|
27
app/controllers/projects/teams_controller.rb
Normal file
27
app/controllers/projects/teams_controller.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
class Projects::TeamsController < Projects::ApplicationController
|
||||
|
||||
def available
|
||||
@teams = current_user.is_admin? ? UserTeam.scoped : current_user.user_teams
|
||||
@teams = @teams.without_project(project)
|
||||
unless @teams.any?
|
||||
redirect_to project_team_index_path(project), notice: "No avaliable teams for assigment."
|
||||
end
|
||||
end
|
||||
|
||||
def assign
|
||||
unless params[:team_id].blank?
|
||||
team = UserTeam.find(params[:team_id])
|
||||
access = params[:greatest_project_access]
|
||||
team.assign_to_project(project, access)
|
||||
end
|
||||
redirect_to project_team_index_path(project)
|
||||
end
|
||||
|
||||
def resign
|
||||
team = project.user_teams.find_by_path(params[:id])
|
||||
team.resign_from_project(project)
|
||||
|
||||
redirect_to project_team_index_path(project)
|
||||
end
|
||||
|
||||
end
|
|
@ -19,7 +19,7 @@ class ProjectsController < ProjectResourceController
|
|||
end
|
||||
|
||||
def create
|
||||
@project = Projects::CreateContext.new(current_user, params[:project]).execute
|
||||
@project = ::Projects::CreateContext.new(current_user, params[:project]).execute
|
||||
|
||||
respond_to do |format|
|
||||
flash[:notice] = 'Project was successfully created.' if @project.saved?
|
||||
|
@ -35,7 +35,7 @@ class ProjectsController < ProjectResourceController
|
|||
end
|
||||
|
||||
def update
|
||||
status = Projects::UpdateContext.new(project, current_user, params).execute
|
||||
status = ::Projects::UpdateContext.new(project, current_user, params).execute
|
||||
|
||||
respond_to do |format|
|
||||
if status
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
class SearchController < ApplicationController
|
||||
def show
|
||||
result = SearchContext.new(current_user.authorized_projects.map(&:id), params).execute
|
||||
project_id = params[:project_id]
|
||||
group_id = params[:group_id]
|
||||
|
||||
project_ids = current_user.authorized_projects.map(&:id)
|
||||
|
||||
if group_id.present?
|
||||
group_project_ids = Group.find(group_id).projects.map(&:id)
|
||||
project_ids.select! { |id| group_project_ids.include?(id)}
|
||||
elsif project_id.present?
|
||||
project_ids.select! { |id| id == project_id.to_i}
|
||||
end
|
||||
|
||||
result = SearchContext.new(project_ids, params).execute
|
||||
|
||||
@projects = result[:projects]
|
||||
@merge_requests = result[:merge_requests]
|
||||
|
|
|
@ -4,15 +4,16 @@ class TeamMembersController < ProjectResourceController
|
|||
before_filter :authorize_admin_project!, except: [:index, :show]
|
||||
|
||||
def index
|
||||
@teams = UserTeam.scoped
|
||||
end
|
||||
|
||||
def show
|
||||
@team_member = project.users_projects.find(params[:id])
|
||||
@events = @team_member.user.recent_events.where(:project_id => @project.id).limit(7)
|
||||
@user_project_relation = project.users_projects.find_by_user_id(member)
|
||||
@events = member.recent_events.in_projects(project).limit(7)
|
||||
end
|
||||
|
||||
def new
|
||||
@team_member = project.users_projects.new
|
||||
@user_project_relation = project.users_projects.new
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -28,18 +29,18 @@ class TeamMembersController < ProjectResourceController
|
|||
end
|
||||
|
||||
def update
|
||||
@team_member = project.users_projects.find(params[:id])
|
||||
@team_member.update_attributes(params[:team_member])
|
||||
@user_project_relation = project.users_projects.find_by_user_id(member)
|
||||
@user_project_relation.update_attributes(params[:team_member])
|
||||
|
||||
unless @team_member.valid?
|
||||
unless @user_project_relation.valid?
|
||||
flash[:alert] = "User should have at least one role"
|
||||
end
|
||||
redirect_to project_team_index_path(@project)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@team_member = project.users_projects.find(params[:id])
|
||||
@team_member.destroy
|
||||
@user_project_relation = project.users_projects.find_by_user_id(member)
|
||||
@user_project_relation.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to project_team_index_path(@project) }
|
||||
|
@ -54,4 +55,10 @@ class TeamMembersController < ProjectResourceController
|
|||
|
||||
redirect_to project_team_members_path(project), notice: notice
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def member
|
||||
@member ||= User.find_by_username(params[:id])
|
||||
end
|
||||
end
|
||||
|
|
13
app/controllers/teams/application_controller.rb
Normal file
13
app/controllers/teams/application_controller.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
class Teams::ApplicationController < ApplicationController
|
||||
|
||||
layout 'user_team'
|
||||
|
||||
before_filter :authorize_manage_user_team!
|
||||
|
||||
protected
|
||||
|
||||
def user_team
|
||||
@team ||= UserTeam.find_by_path(params[:team_id])
|
||||
end
|
||||
|
||||
end
|
49
app/controllers/teams/members_controller.rb
Normal file
49
app/controllers/teams/members_controller.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
class Teams::MembersController < Teams::ApplicationController
|
||||
|
||||
skip_before_filter :authorize_manage_user_team!, only: [:index]
|
||||
|
||||
def index
|
||||
@members = user_team.members
|
||||
end
|
||||
|
||||
def new
|
||||
@users = User.potential_team_members(user_team)
|
||||
@users = UserDecorator.decorate @users
|
||||
end
|
||||
|
||||
def create
|
||||
unless params[:user_ids].blank?
|
||||
user_ids = params[:user_ids]
|
||||
access = params[:default_project_access]
|
||||
is_admin = params[:group_admin]
|
||||
user_team.add_members(user_ids, access, is_admin)
|
||||
end
|
||||
|
||||
redirect_to team_members_path(user_team), notice: 'Members was successfully added into Team of users.'
|
||||
end
|
||||
|
||||
def edit
|
||||
team_member
|
||||
end
|
||||
|
||||
def update
|
||||
options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]}
|
||||
if user_team.update_membership(team_member, options)
|
||||
redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users."
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
user_team.remove_member(team_member)
|
||||
redirect_to team_path(user_team), notice: "Member #{team_member.name} was successfully removed from Team of users."
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def team_member
|
||||
@member ||= user_team.members.find_by_username(params[:id])
|
||||
end
|
||||
|
||||
end
|
57
app/controllers/teams/projects_controller.rb
Normal file
57
app/controllers/teams/projects_controller.rb
Normal file
|
@ -0,0 +1,57 @@
|
|||
class Teams::ProjectsController < Teams::ApplicationController
|
||||
|
||||
skip_before_filter :authorize_manage_user_team!, only: [:index]
|
||||
|
||||
def index
|
||||
@projects = user_team.projects
|
||||
@avaliable_projects = current_user.admin? ? Project.without_team(user_team) : current_user.owned_projects.without_team(user_team)
|
||||
end
|
||||
|
||||
def new
|
||||
user_team
|
||||
@avaliable_projects = current_user.owned_projects.scoped
|
||||
@avaliable_projects = @avaliable_projects.without_team(user_team) if user_team.projects.any?
|
||||
|
||||
redirect_to team_projects_path(user_team), notice: "No avalible projects." unless @avaliable_projects.any?
|
||||
end
|
||||
|
||||
def create
|
||||
redirect_to :back if params[:project_ids].blank?
|
||||
|
||||
project_ids = params[:project_ids]
|
||||
access = params[:greatest_project_access]
|
||||
|
||||
# Reject non-allowed projects
|
||||
allowed_project_ids = current_user.owned_projects.map(&:id)
|
||||
project_ids.select! { |id| allowed_project_ids.include?(id.to_i) }
|
||||
|
||||
# Assign projects to team
|
||||
user_team.assign_to_projects(project_ids, access)
|
||||
|
||||
redirect_to team_projects_path(user_team), notice: 'Team of users was successfully assigned to projects.'
|
||||
end
|
||||
|
||||
def edit
|
||||
team_project
|
||||
end
|
||||
|
||||
def update
|
||||
if user_team.update_project_access(team_project, params[:greatest_project_access])
|
||||
redirect_to team_projects_path(user_team), notice: 'Access was successfully updated.'
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
user_team.resign_from_project(team_project)
|
||||
redirect_to team_projects_path(user_team), notice: 'Team of users was successfully reassigned from project.'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def team_project
|
||||
@project ||= user_team.projects.find_with_namespace(params[:id])
|
||||
end
|
||||
|
||||
end
|
76
app/controllers/teams_controller.rb
Normal file
76
app/controllers/teams_controller.rb
Normal file
|
@ -0,0 +1,76 @@
|
|||
class TeamsController < ApplicationController
|
||||
# Authorize
|
||||
before_filter :authorize_create_team!, only: [:new, :create]
|
||||
before_filter :authorize_manage_user_team!, only: [:edit, :update]
|
||||
before_filter :authorize_admin_user_team!, only: [:destroy]
|
||||
|
||||
before_filter :user_team, except: [:new, :create]
|
||||
|
||||
layout 'user_team', except: [:new, :create]
|
||||
|
||||
def show
|
||||
user_team
|
||||
projects
|
||||
@events = Event.in_projects(user_team.project_ids).limit(20).offset(params[:offset] || 0)
|
||||
end
|
||||
|
||||
def edit
|
||||
user_team
|
||||
end
|
||||
|
||||
def update
|
||||
if user_team.update_attributes(params[:user_team])
|
||||
redirect_to team_path(user_team)
|
||||
else
|
||||
render action: :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
user_team.destroy
|
||||
redirect_to dashboard_path
|
||||
end
|
||||
|
||||
def new
|
||||
@team = UserTeam.new
|
||||
end
|
||||
|
||||
def create
|
||||
@team = UserTeam.new(params[:user_team])
|
||||
@team.owner = current_user unless params[:owner]
|
||||
@team.path = @team.name.dup.parameterize if @team.name
|
||||
|
||||
if @team.save
|
||||
redirect_to team_path(@team)
|
||||
else
|
||||
render action: :new
|
||||
end
|
||||
end
|
||||
|
||||
# Get authored or assigned open merge requests
|
||||
def merge_requests
|
||||
projects
|
||||
@merge_requests = MergeRequest.of_user_team(user_team)
|
||||
@merge_requests = FilterContext.new(@merge_requests, params).execute
|
||||
@merge_requests = @merge_requests.recent.page(params[:page]).per(20)
|
||||
end
|
||||
|
||||
# Get only assigned issues
|
||||
def issues
|
||||
projects
|
||||
@issues = Issue.of_user_team(user_team)
|
||||
@issues = FilterContext.new(@issues, params).execute
|
||||
@issues = @issues.recent.page(params[:page]).per(20)
|
||||
@issues = @issues.includes(:author, :project)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def projects
|
||||
@projects ||= user_team.projects.sorted_by_activity
|
||||
end
|
||||
|
||||
def user_team
|
||||
@team ||= current_user.authorized_teams.find_by_path(params[:id])
|
||||
end
|
||||
end
|
|
@ -8,4 +8,8 @@ class UserDecorator < ApplicationDecorator
|
|||
def tm_of(project)
|
||||
project.team_member_by_id(self.id)
|
||||
end
|
||||
|
||||
def name_with_email
|
||||
"#{name} (#{email})"
|
||||
end
|
||||
end
|
||||
|
|
5
app/helpers/admin/teams/members_helper.rb
Normal file
5
app/helpers/admin/teams/members_helper.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
module Admin::Teams::MembersHelper
|
||||
def member_since(team, member)
|
||||
team.user_team_user_relationships.find_by_user_id(member).created_at
|
||||
end
|
||||
end
|
5
app/helpers/admin/teams/projects_helper.rb
Normal file
5
app/helpers/admin/teams/projects_helper.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
module Admin::Teams::ProjectsHelper
|
||||
def assigned_since(team, project)
|
||||
team.user_team_project_relationships.find_by_project_id(project).created_at
|
||||
end
|
||||
end
|
|
@ -72,8 +72,9 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def search_autocomplete_source
|
||||
projects = current_user.authorized_projects.map { |p| { label: p.name_with_namespace, url: project_path(p) } }
|
||||
groups = current_user.authorized_groups.map { |group| { label: "<group> #{group.name}", url: group_path(group) } }
|
||||
projects = current_user.authorized_projects.map { |p| { label: "project: #{p.name_with_namespace}", url: project_path(p) } }
|
||||
groups = current_user.authorized_groups.map { |group| { label: "group: #{group.name}", url: group_path(group) } }
|
||||
teams = current_user.authorized_teams.map { |team| { label: "team: #{team.name}", url: team_path(team) } }
|
||||
|
||||
default_nav = [
|
||||
{ label: "My Profile", url: profile_path },
|
||||
|
@ -83,29 +84,29 @@ module ApplicationHelper
|
|||
]
|
||||
|
||||
help_nav = [
|
||||
{ label: "API Help", url: help_api_path },
|
||||
{ label: "Markdown Help", url: help_markdown_path },
|
||||
{ label: "Permissions Help", url: help_permissions_path },
|
||||
{ label: "Public Access Help", url: help_public_access_path },
|
||||
{ label: "Rake Tasks Help", url: help_raketasks_path },
|
||||
{ label: "SSH Keys Help", url: help_ssh_path },
|
||||
{ label: "System Hooks Help", url: help_system_hooks_path },
|
||||
{ label: "Web Hooks Help", url: help_web_hooks_path },
|
||||
{ label: "Workflow Help", url: help_workflow_path },
|
||||
{ label: "help: API Help", url: help_api_path },
|
||||
{ label: "help: Markdown Help", url: help_markdown_path },
|
||||
{ label: "help: Permissions Help", url: help_permissions_path },
|
||||
{ label: "help: Public Access Help", url: help_public_access_path },
|
||||
{ label: "help: Rake Tasks Help", url: help_raketasks_path },
|
||||
{ label: "help: SSH Keys Help", url: help_ssh_path },
|
||||
{ label: "help: System Hooks Help", url: help_system_hooks_path },
|
||||
{ label: "help: Web Hooks Help", url: help_web_hooks_path },
|
||||
{ label: "help: Workflow Help", url: help_workflow_path },
|
||||
]
|
||||
|
||||
project_nav = []
|
||||
if @project && @project.repository && @project.repository.root_ref
|
||||
project_nav = [
|
||||
{ label: "#{@project.name} Issues", url: project_issues_path(@project) },
|
||||
{ label: "#{@project.name} Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
|
||||
{ label: "#{@project.name} Merge Requests", url: project_merge_requests_path(@project) },
|
||||
{ label: "#{@project.name} Milestones", url: project_milestones_path(@project) },
|
||||
{ label: "#{@project.name} Snippets", url: project_snippets_path(@project) },
|
||||
{ label: "#{@project.name} Team", url: project_team_index_path(@project) },
|
||||
{ label: "#{@project.name} Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) },
|
||||
{ label: "#{@project.name} Wall", url: wall_project_path(@project) },
|
||||
{ label: "#{@project.name} Wiki", url: project_wikis_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Issues", url: project_issues_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
|
||||
{ label: "#{@project.name_with_namespace} - Merge Requests", url: project_merge_requests_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Milestones", url: project_milestones_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Snippets", url: project_snippets_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Team", url: project_team_index_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Tree", url: project_tree_path(@project, @ref || @project.repository.root_ref) },
|
||||
{ label: "#{@project.name_with_namespace} - Wall", url: wall_project_path(@project) },
|
||||
{ label: "#{@project.name_with_namespace} - Wiki", url: project_wikis_path(@project) },
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -59,9 +59,9 @@ module CommitsHelper
|
|||
|
||||
def image_diff_class(diff)
|
||||
if diff.deleted_file
|
||||
"diff_removed"
|
||||
"deleted"
|
||||
elsif diff.new_file
|
||||
"diff_added"
|
||||
"added"
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
|
|
@ -9,9 +9,9 @@ module DashboardHelper
|
|||
|
||||
case entity
|
||||
when 'issue' then
|
||||
dashboard_issues_path(options)
|
||||
issues_dashboard_path(options)
|
||||
when 'merge_request'
|
||||
dashboard_merge_requests_path(options)
|
||||
merge_requests_dashboard_path(options)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,8 +3,12 @@ module ProjectsHelper
|
|||
@project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
|
||||
end
|
||||
|
||||
def remove_from_team_message(project, member)
|
||||
"You are going to remove #{member.user_name} from #{project.name}. Are you sure?"
|
||||
def grouper_project_teams(project)
|
||||
@project.user_team_project_relationships.sort_by(&:greatest_access).reverse.group_by(&:greatest_access)
|
||||
end
|
||||
|
||||
def remove_from_project_team_message(project, user)
|
||||
"You are going to remove #{user.name} from #{project.name} project team. Are you sure?"
|
||||
end
|
||||
|
||||
def link_to_project project
|
||||
|
@ -51,7 +55,9 @@ module ProjectsHelper
|
|||
|
||||
def project_title project
|
||||
if project.group
|
||||
project.name_with_namespace
|
||||
content_tag :span do
|
||||
link_to(project.group.name, group_path(project.group)) + " / " + project.name
|
||||
end
|
||||
else
|
||||
project.name
|
||||
end
|
||||
|
|
|
@ -39,7 +39,12 @@ module TabHelper
|
|||
# Returns a list item element String
|
||||
def nav_link(options = {}, &block)
|
||||
if path = options.delete(:path)
|
||||
c, a, _ = path.split('#')
|
||||
if path.respond_to?(:each)
|
||||
c = path.map { |p| p.split('#').first }
|
||||
a = path.map { |p| p.split('#').last }
|
||||
else
|
||||
c, a, _ = path.split('#')
|
||||
end
|
||||
else
|
||||
c = options.delete(:controller)
|
||||
a = options.delete(:action)
|
||||
|
|
26
app/helpers/user_teams_helper.rb
Normal file
26
app/helpers/user_teams_helper.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
module UserTeamsHelper
|
||||
def team_filter_path(entity, options={})
|
||||
exist_opts = {
|
||||
status: params[:status],
|
||||
project_id: params[:project_id],
|
||||
}
|
||||
|
||||
options = exist_opts.merge(options)
|
||||
|
||||
case entity
|
||||
when 'issue' then
|
||||
issues_team_path(@team, options)
|
||||
when 'merge_request'
|
||||
merge_requests_team_path(@team, options)
|
||||
end
|
||||
end
|
||||
|
||||
def grouped_user_team_members(team)
|
||||
team.user_team_user_relationships.sort_by(&:permission).reverse.group_by(&:permission)
|
||||
end
|
||||
|
||||
def remove_from_user_team_message(team, member)
|
||||
"You are going to remove #{member.name} from #{team.name}. Are you sure?"
|
||||
end
|
||||
|
||||
end
|
|
@ -1,15 +1,25 @@
|
|||
class Ability
|
||||
class << self
|
||||
def allowed(object, subject)
|
||||
def allowed(user, subject)
|
||||
return [] unless user.kind_of?(User)
|
||||
|
||||
case subject.class.name
|
||||
when "Project" then project_abilities(object, subject)
|
||||
when "Issue" then issue_abilities(object, subject)
|
||||
when "Note" then note_abilities(object, subject)
|
||||
when "Snippet" then snippet_abilities(object, subject)
|
||||
when "MergeRequest" then merge_request_abilities(object, subject)
|
||||
when "Group", "Namespace" then group_abilities(object, subject)
|
||||
when "Project" then project_abilities(user, subject)
|
||||
when "Issue" then issue_abilities(user, subject)
|
||||
when "Note" then note_abilities(user, subject)
|
||||
when "Snippet" then snippet_abilities(user, subject)
|
||||
when "MergeRequest" then merge_request_abilities(user, subject)
|
||||
when "Group", "Namespace" then group_abilities(user, subject)
|
||||
when "UserTeam" then user_team_abilities(user, subject)
|
||||
else []
|
||||
end
|
||||
end.concat(global_abilities(user))
|
||||
end
|
||||
|
||||
def global_abilities(user)
|
||||
rules = []
|
||||
rules << :create_group if user.can_create_group
|
||||
rules << :create_team if user.can_create_team
|
||||
rules
|
||||
end
|
||||
|
||||
def project_abilities(user, project)
|
||||
|
@ -110,6 +120,22 @@ class Ability
|
|||
rules.flatten
|
||||
end
|
||||
|
||||
def user_team_abilities user, team
|
||||
rules = []
|
||||
|
||||
# Only group owner and administrators can manage group
|
||||
if team.owner == user || team.admin?(user) || user.admin?
|
||||
rules << [ :manage_user_team ]
|
||||
end
|
||||
|
||||
if team.owner == user || user.admin?
|
||||
rules << [ :admin_user_team ]
|
||||
end
|
||||
|
||||
rules.flatten
|
||||
end
|
||||
|
||||
|
||||
[:issue, :note, :snippet, :merge_request].each do |name|
|
||||
define_method "#{name}_abilities" do |user, subject|
|
||||
if subject.author == user
|
||||
|
|
|
@ -22,6 +22,7 @@ module Issuable
|
|||
scope :opened, where(closed: false)
|
||||
scope :closed, where(closed: true)
|
||||
scope :of_group, ->(group) { where(project_id: group.project_ids) }
|
||||
scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
|
||||
scope :assigned, ->(u) { where(assignee_id: u.id)}
|
||||
scope :recent, order("created_at DESC")
|
||||
|
||||
|
|
|
@ -33,28 +33,31 @@ class Project < ActiveRecord::Base
|
|||
attr_accessor :error_code
|
||||
|
||||
# Relations
|
||||
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
|
||||
belongs_to :creator, foreign_key: "creator_id", class_name: "User"
|
||||
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
|
||||
belongs_to :namespace
|
||||
|
||||
belongs_to :creator,
|
||||
class_name: "User",
|
||||
foreign_key: "creator_id"
|
||||
|
||||
has_many :users, through: :users_projects
|
||||
has_many :events, dependent: :destroy
|
||||
has_many :merge_requests, dependent: :destroy
|
||||
has_many :issues, dependent: :destroy, order: "closed, created_at DESC"
|
||||
has_many :milestones, dependent: :destroy
|
||||
has_many :users_projects, dependent: :destroy
|
||||
has_many :notes, dependent: :destroy
|
||||
has_many :snippets, dependent: :destroy
|
||||
has_many :deploy_keys, dependent: :destroy, foreign_key: "project_id", class_name: "Key"
|
||||
has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
|
||||
has_many :wikis, dependent: :destroy
|
||||
has_many :protected_branches, dependent: :destroy
|
||||
has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id'
|
||||
has_one :gitlab_ci_service, dependent: :destroy
|
||||
|
||||
has_many :events, dependent: :destroy
|
||||
has_many :merge_requests, dependent: :destroy
|
||||
has_many :issues, dependent: :destroy, order: "closed, created_at DESC"
|
||||
has_many :milestones, dependent: :destroy
|
||||
has_many :users_projects, dependent: :destroy
|
||||
has_many :notes, dependent: :destroy
|
||||
has_many :snippets, dependent: :destroy
|
||||
has_many :deploy_keys, dependent: :destroy, class_name: "Key", foreign_key: "project_id"
|
||||
has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
|
||||
has_many :wikis, dependent: :destroy
|
||||
has_many :protected_branches, dependent: :destroy
|
||||
has_many :user_team_project_relationships, dependent: :destroy
|
||||
|
||||
has_many :users, through: :users_projects
|
||||
has_many :user_teams, through: :user_team_project_relationships
|
||||
has_many :user_team_user_relationships, through: :user_teams
|
||||
has_many :user_teams_members, through: :user_team_user_relationships
|
||||
|
||||
delegate :name, to: :owner, allow_nil: true, prefix: true
|
||||
|
||||
# Validations
|
||||
|
@ -77,6 +80,8 @@ class Project < ActiveRecord::Base
|
|||
# Scopes
|
||||
scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
|
||||
scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) }
|
||||
scope :without_team, ->(team) { team.projects.present? ? where("id NOT IN (:ids)", ids: team.projects.map(&:id)) : scoped }
|
||||
scope :in_team, ->(team) { where("id IN (:ids)", ids: team.projects.map(&:id)) }
|
||||
scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) }
|
||||
scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") }
|
||||
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
|
||||
|
@ -122,7 +127,7 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def team
|
||||
@team ||= Team.new(self)
|
||||
@team ||= ProjectTeam.new(self)
|
||||
end
|
||||
|
||||
def repository
|
||||
|
@ -335,7 +340,7 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def execute_hooks(data)
|
||||
hooks.each { |hook| hook.execute(data) }
|
||||
hooks.each { |hook| hook.async_execute(data) }
|
||||
end
|
||||
|
||||
def execute_services(data)
|
||||
|
@ -489,6 +494,11 @@ class Project < ActiveRecord::Base
|
|||
http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
|
||||
end
|
||||
|
||||
def project_access_human(member)
|
||||
project_user_relation = self.users_projects.find_by_user_id(member.id)
|
||||
self.class.access_options.key(project_user_relation.project_access)
|
||||
end
|
||||
|
||||
# Check if current branch name is marked as protected in the system
|
||||
def protected_branch? branch_name
|
||||
protected_branches.map(&:name).include?(branch_name)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class Team
|
||||
class ProjectTeam
|
||||
attr_accessor :project
|
||||
|
||||
def initialize(project)
|
|
@ -40,23 +40,32 @@ class User < ActiveRecord::Base
|
|||
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
|
||||
:skype, :linkedin, :twitter, :dark_scheme, :theme_id, :force_random_password,
|
||||
:extern_uid, :provider, as: [:default, :admin]
|
||||
attr_accessible :projects_limit, as: :admin
|
||||
attr_accessible :projects_limit, :can_create_team, :can_create_group, as: :admin
|
||||
|
||||
attr_accessor :force_random_password
|
||||
|
||||
# Namespace for personal projects
|
||||
has_one :namespace, class_name: "Namespace", foreign_key: :owner_id, conditions: 'type IS NULL', dependent: :destroy
|
||||
has_many :groups, class_name: "Group", foreign_key: :owner_id
|
||||
has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL'
|
||||
|
||||
has_many :keys, dependent: :destroy
|
||||
has_many :users_projects, dependent: :destroy
|
||||
has_many :issues, foreign_key: :author_id, dependent: :destroy
|
||||
has_many :notes, foreign_key: :author_id, dependent: :destroy
|
||||
has_many :merge_requests, foreign_key: :author_id, dependent: :destroy
|
||||
has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy
|
||||
has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"
|
||||
has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy
|
||||
has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy
|
||||
has_many :keys, dependent: :destroy
|
||||
has_many :users_projects, dependent: :destroy
|
||||
has_many :issues, dependent: :destroy, foreign_key: :author_id
|
||||
has_many :notes, dependent: :destroy, foreign_key: :author_id
|
||||
has_many :merge_requests, dependent: :destroy, foreign_key: :author_id
|
||||
has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event"
|
||||
has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
|
||||
has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
|
||||
|
||||
has_many :groups, class_name: "Group", foreign_key: :owner_id
|
||||
has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"
|
||||
|
||||
has_many :projects, through: :users_projects
|
||||
|
||||
has_many :user_team_user_relationships, dependent: :destroy
|
||||
|
||||
has_many :user_teams, through: :user_team_user_relationships
|
||||
has_many :user_team_project_relationships, through: :user_teams
|
||||
has_many :team_projects, through: :user_team_project_relationships
|
||||
|
||||
validates :name, presence: true
|
||||
validates :bio, length: { within: 0..255 }
|
||||
|
@ -80,6 +89,9 @@ class User < ActiveRecord::Base
|
|||
scope :blocked, where(blocked: true)
|
||||
scope :active, where(blocked: false)
|
||||
scope :alphabetically, order('name ASC')
|
||||
scope :in_team, ->(team){ where(id: team.member_ids) }
|
||||
scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
|
||||
scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active }
|
||||
|
||||
#
|
||||
# Class methods
|
||||
|
@ -131,6 +143,11 @@ class User < ActiveRecord::Base
|
|||
#
|
||||
# Instance methods
|
||||
#
|
||||
|
||||
def to_param
|
||||
username
|
||||
end
|
||||
|
||||
def generate_password
|
||||
if self.force_random_password
|
||||
self.password = self.password_confirmation = Devise.friendly_token.first(8)
|
||||
|
@ -220,7 +237,7 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def can_create_group?
|
||||
is_admin?
|
||||
can?(:create_group, nil)
|
||||
end
|
||||
|
||||
def abilities
|
||||
|
@ -283,4 +300,15 @@ class User < ActiveRecord::Base
|
|||
def namespace_id
|
||||
namespace.try :id
|
||||
end
|
||||
|
||||
def authorized_teams
|
||||
@authorized_teams ||= begin
|
||||
ids = []
|
||||
ids << UserTeam.with_member(self).pluck('user_teams.id')
|
||||
ids << UserTeam.created_by(self).pluck('user_teams.id')
|
||||
ids.flatten
|
||||
|
||||
UserTeam.where(id: ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
97
app/models/user_team.rb
Normal file
97
app/models/user_team.rb
Normal file
|
@ -0,0 +1,97 @@
|
|||
class UserTeam < ActiveRecord::Base
|
||||
attr_accessible :name, :owner_id, :path
|
||||
|
||||
belongs_to :owner, class_name: User
|
||||
|
||||
has_many :user_team_project_relationships, dependent: :destroy
|
||||
has_many :user_team_user_relationships, dependent: :destroy
|
||||
|
||||
has_many :projects, through: :user_team_project_relationships
|
||||
has_many :members, through: :user_team_user_relationships, source: :user
|
||||
|
||||
validates :name, presence: true, uniqueness: true
|
||||
validates :owner, presence: true
|
||||
validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
|
||||
format: { with: Gitlab::Regex.path_regex,
|
||||
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
|
||||
|
||||
scope :with_member, ->(user){ joins(:user_team_user_relationships).where(user_team_user_relationships: {user_id: user.id}) }
|
||||
scope :with_project, ->(project){ joins(:user_team_project_relationships).where(user_team_project_relationships: {project_id: project})}
|
||||
scope :without_project, ->(project){ where("user_teams.id NOT IN (:ids)", ids: (a = with_project(project); a.blank? ? 0 : a))}
|
||||
scope :created_by, ->(user){ where(owner_id: user) }
|
||||
|
||||
class << self
|
||||
def search query
|
||||
where("name LIKE :query OR path LIKE :query", query: "%#{query}%")
|
||||
end
|
||||
|
||||
def global_id
|
||||
'GLN'
|
||||
end
|
||||
|
||||
def access_roles
|
||||
UsersProject.access_roles
|
||||
end
|
||||
end
|
||||
|
||||
def to_param
|
||||
path
|
||||
end
|
||||
|
||||
def assign_to_projects(projects, access)
|
||||
projects.each do |project|
|
||||
assign_to_project(project, access)
|
||||
end
|
||||
end
|
||||
|
||||
def assign_to_project(project, access)
|
||||
Gitlab::UserTeamManager.assign(self, project, access)
|
||||
end
|
||||
|
||||
def resign_from_project(project)
|
||||
Gitlab::UserTeamManager.resign(self, project)
|
||||
end
|
||||
|
||||
def add_members(users, access, group_admin)
|
||||
users.each do |user|
|
||||
add_member(user, access, group_admin)
|
||||
end
|
||||
end
|
||||
|
||||
def add_member(user, access, group_admin)
|
||||
Gitlab::UserTeamManager.add_member_into_team(self, user, access, group_admin)
|
||||
end
|
||||
|
||||
def remove_member(user)
|
||||
Gitlab::UserTeamManager.remove_member_from_team(self, user)
|
||||
end
|
||||
|
||||
def update_membership(user, options)
|
||||
Gitlab::UserTeamManager.update_team_user_membership(self, user, options)
|
||||
end
|
||||
|
||||
def update_project_access(project, permission)
|
||||
Gitlab::UserTeamManager.update_project_greates_access(self, project, permission)
|
||||
end
|
||||
|
||||
def max_project_access(project)
|
||||
user_team_project_relationships.find_by_project_id(project).greatest_access
|
||||
end
|
||||
|
||||
def human_max_project_access(project)
|
||||
self.class.access_roles.invert[max_project_access(project)]
|
||||
end
|
||||
|
||||
def default_projects_access(member)
|
||||
user_team_user_relationships.find_by_user_id(member).permission
|
||||
end
|
||||
|
||||
def human_default_projects_access(member)
|
||||
self.class.access_roles.invert[default_projects_access(member)]
|
||||
end
|
||||
|
||||
def admin?(member)
|
||||
user_team_user_relationships.with_user(member).first.group_admin?
|
||||
end
|
||||
|
||||
end
|
28
app/models/user_team_project_relationship.rb
Normal file
28
app/models/user_team_project_relationship.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
class UserTeamProjectRelationship < ActiveRecord::Base
|
||||
attr_accessible :greatest_access, :project_id, :user_team_id
|
||||
|
||||
belongs_to :user_team
|
||||
belongs_to :project
|
||||
|
||||
validates :project, presence: true
|
||||
validates :user_team, presence: true
|
||||
validate :check_greatest_access
|
||||
|
||||
scope :with_project, ->(project){ where(project_id: project.id) }
|
||||
|
||||
def team_name
|
||||
user_team.name
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_greatest_access
|
||||
errors.add(:base, :incorrect_access_code) unless correct_access?
|
||||
end
|
||||
|
||||
def correct_access?
|
||||
return false if greatest_access.blank?
|
||||
return true if UsersProject.access_roles.has_value?(greatest_access)
|
||||
false
|
||||
end
|
||||
end
|
19
app/models/user_team_user_relationship.rb
Normal file
19
app/models/user_team_user_relationship.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
class UserTeamUserRelationship < ActiveRecord::Base
|
||||
attr_accessible :group_admin, :permission, :user_id, :user_team_id
|
||||
|
||||
belongs_to :user_team
|
||||
belongs_to :user
|
||||
|
||||
validates :user_team, presence: true
|
||||
validates :user, presence: true
|
||||
|
||||
scope :with_user, ->(user) { where(user_id: user.id) }
|
||||
|
||||
def user_name
|
||||
user.name
|
||||
end
|
||||
|
||||
def access_human
|
||||
UsersProject.access_roles.invert[permission]
|
||||
end
|
||||
end
|
|
@ -39,7 +39,10 @@ class UsersProject < ActiveRecord::Base
|
|||
scope :reporters, where(project_access: REPORTER)
|
||||
scope :developers, where(project_access: DEVELOPER)
|
||||
scope :masters, where(project_access: MASTER)
|
||||
|
||||
scope :in_project, ->(project) { where(project_id: project.id) }
|
||||
scope :in_projects, ->(projects) { where(project_id: project_ids) }
|
||||
scope :with_user, ->(user) { where(user_id: user.id) }
|
||||
|
||||
class << self
|
||||
|
||||
|
|
|
@ -34,4 +34,8 @@ class WebHook < ActiveRecord::Base
|
|||
basic_auth: {username: parsed_url.user, password: parsed_url.password})
|
||||
end
|
||||
end
|
||||
|
||||
def async_execute(data)
|
||||
Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,16 +72,17 @@
|
|||
%th Users
|
||||
%th Project Access:
|
||||
|
||||
- @group.users.each do |u|
|
||||
%tr{class: "user_#{u.id}"}
|
||||
%td.name= link_to u.name, admin_user_path(u)
|
||||
- @group.users.each do |user|
|
||||
- next unless user
|
||||
%tr{class: "user_#{user.id}"}
|
||||
%td.name= link_to user.name, admin_user_path(user)
|
||||
%td.projects_access
|
||||
- u.authorized_projects.in_namespace(@group).each do |project|
|
||||
- u_p = u.users_projects.in_project(project).first
|
||||
- user.authorized_projects.in_namespace(@group).each do |project|
|
||||
- u_p = user.users_projects.in_project(project).first
|
||||
- next unless u_p
|
||||
%span
|
||||
= project.name
|
||||
= link_to "(#{ u_p.project_access_human })", edit_admin_team_member_path(u_p)
|
||||
= project.name_with_namespace
|
||||
= link_to "(#{ u_p.project_access_human })", edit_admin_project_member_path(project, user)
|
||||
%tr
|
||||
%td.input= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
|
||||
%td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
|
||||
|
|
16
app/views/admin/projects/members/_form.html.haml
Normal file
16
app/views/admin/projects/members/_form.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
|||
= form_for @team_member_relation, as: :team_member, url: admin_project_member_path(@project, @member) do |f|
|
||||
-if @team_member_relation.errors.any?
|
||||
.alert-message.block-message.error
|
||||
%ul
|
||||
- @team_member_relation.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
.clearfix
|
||||
%label Project Access:
|
||||
.input
|
||||
= f.select :project_access, options_for_select(Project.access_options, @team_member_relation.project_access), {}, class: "project-access-select chosen span3"
|
||||
|
||||
%br
|
||||
.actions
|
||||
= f.submit 'Save', class: "btn primary"
|
||||
= link_to 'Cancel', :back, class: "btn"
|
8
app/views/admin/projects/members/edit.html.haml
Normal file
8
app/views/admin/projects/members/edit.html.haml
Normal file
|
@ -0,0 +1,8 @@
|
|||
%p.slead
|
||||
Edit access for
|
||||
= link_to @member.name, admin_user_path(@member)
|
||||
in
|
||||
= link_to @project.name_with_namespace, admin_project_path(@project)
|
||||
|
||||
%hr
|
||||
= render 'form'
|
|
@ -114,9 +114,9 @@
|
|||
%h5
|
||||
Team
|
||||
%small
|
||||
(#{@project.users_projects.count})
|
||||
(#{@project.users.count})
|
||||
%br
|
||||
%table.zebra-striped
|
||||
%table.zebra-striped.team_members
|
||||
%thead
|
||||
%tr
|
||||
%th Name
|
||||
|
@ -124,13 +124,13 @@
|
|||
%th Repository Access
|
||||
%th
|
||||
|
||||
- @project.users_projects.each do |tm|
|
||||
- @project.users.each do |tm|
|
||||
%tr
|
||||
%td
|
||||
= link_to tm.user_name, admin_user_path(tm.user)
|
||||
%td= tm.project_access_human
|
||||
%td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
|
||||
%td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small"
|
||||
= link_to tm.name, admin_user_path(tm)
|
||||
%td= @project.project_access_human(tm)
|
||||
%td= link_to 'Edit Access', edit_admin_project_member_path(@project, tm), class: "btn small"
|
||||
%td= link_to 'Remove from team', admin_project_member_path(@project, tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small"
|
||||
|
||||
%br
|
||||
%h5 Add new team member
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
= form_for @admin_team_member, as: :team_member, url: admin_team_member_path(@admin_team_member) do |f|
|
||||
-if @admin_team_member.errors.any?
|
||||
.alert-message.block-message.error
|
||||
%ul
|
||||
- @admin_team_member.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
.clearfix
|
||||
%label Project Access:
|
||||
.input
|
||||
= f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select chosen span3"
|
||||
|
||||
%br
|
||||
.actions
|
||||
= f.submit 'Save', class: "btn primary"
|
||||
= link_to 'Cancel', :back, class: "btn"
|
|
@ -1,8 +0,0 @@
|
|||
%p.slead
|
||||
Edit access for
|
||||
= link_to @admin_team_member.user_name, admin_user_path(@admin_team_member)
|
||||
in
|
||||
= link_to @admin_team_member.project.name_with_namespace, admin_project_path(@admin_team_member)
|
||||
|
||||
%hr
|
||||
= render 'form'
|
23
app/views/admin/teams/edit.html.haml
Normal file
23
app/views/admin/teams/edit.html.haml
Normal file
|
@ -0,0 +1,23 @@
|
|||
%h3.page_title Rename Team
|
||||
%hr
|
||||
= form_for @team, url: admin_team_path(@team), method: :put do |f|
|
||||
- if @team.errors.any?
|
||||
.alert-message.block-message.error
|
||||
%span= @team.errors.full_messages.first
|
||||
.clearfix.team_name_holder
|
||||
= f.label :name do
|
||||
Team name is
|
||||
.input
|
||||
= f.text_field :name, placeholder: "Example Team", class: "xxlarge"
|
||||
|
||||
.clearfix.team_name_holder
|
||||
= f.label :path do
|
||||
%span.cred Team path is
|
||||
.input
|
||||
= f.text_field :path, placeholder: "example-team", class: "xxlarge danger"
|
||||
%ul.cred
|
||||
%li It will change web url for access team and team projects.
|
||||
|
||||
.form-actions
|
||||
= f.submit 'Rename team', class: "btn danger"
|
||||
= link_to 'Cancel', admin_teams_path, class: "btn cancel-btn"
|
38
app/views/admin/teams/index.html.haml
Normal file
38
app/views/admin/teams/index.html.haml
Normal file
|
@ -0,0 +1,38 @@
|
|||
%h3.page_title
|
||||
Teams
|
||||
%small
|
||||
simple Teams description
|
||||
|
||||
= link_to 'New Team', new_admin_team_path, class: "btn small right"
|
||||
%br
|
||||
|
||||
= form_tag admin_teams_path, method: :get, class: 'form-inline' do
|
||||
= text_field_tag :name, params[:name], class: "xlarge"
|
||||
= submit_tag "Search", class: "btn submit primary"
|
||||
|
||||
%table
|
||||
%thead
|
||||
%tr
|
||||
%th
|
||||
Name
|
||||
%i.icon-sort-down
|
||||
%th Path
|
||||
%th Projects
|
||||
%th Members
|
||||
%th Owner
|
||||
%th.cred Danger Zone!
|
||||
|
||||
- @teams.each do |team|
|
||||
%tr
|
||||
%td
|
||||
%strong= link_to team.name, admin_team_path(team)
|
||||
%td= team.path
|
||||
%td= team.projects.count
|
||||
%td= team.members.count
|
||||
%td
|
||||
= link_to team.owner.name, admin_user_path(team.owner_id)
|
||||
%td.bgred
|
||||
= link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn small"
|
||||
= link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn small danger"
|
||||
|
||||
= paginate @teams, theme: "admin"
|
20
app/views/admin/teams/members/_form.html.haml
Normal file
20
app/views/admin/teams/members/_form.html.haml
Normal file
|
@ -0,0 +1,20 @@
|
|||
= form_tag admin_team_member_path(@team, @member), method: :put do
|
||||
-if @member.errors.any?
|
||||
.alert-message.block-message.error
|
||||
%ul
|
||||
- @member.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
.clearfix
|
||||
%label Default access for Team projects:
|
||||
.input
|
||||
= select_tag :default_project_access, options_for_select(UserTeam.access_roles, @team.default_projects_access(@member)), class: "project-access-select chosen span3"
|
||||
.clearfix
|
||||
%label Team admin?
|
||||
.input
|
||||
= check_box_tag :group_admin, true, @team.admin?(@member)
|
||||
|
||||
%br
|
||||
.actions
|
||||
= submit_tag 'Save', class: "btn primary"
|
||||
= link_to 'Cancel', :back, class: "btn"
|
16
app/views/admin/teams/members/edit.html.haml
Normal file
16
app/views/admin/teams/members/edit.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
|||
%h3
|
||||
Edit access #{@member.name} in #{@team.name} team
|
||||
|
||||
%hr
|
||||
%table.zebra-striped
|
||||
%tr
|
||||
%td User:
|
||||
%td= @member.name
|
||||
%tr
|
||||
%td Team:
|
||||
%td= @team.name
|
||||
%tr
|
||||
%td Since:
|
||||
%td= member_since(@team, @member).stamp("Nov 11, 2010")
|
||||
|
||||
= render 'form'
|
29
app/views/admin/teams/members/new.html.haml
Normal file
29
app/views/admin/teams/members/new.html.haml
Normal file
|
@ -0,0 +1,29 @@
|
|||
%h3.page_title
|
||||
Team: #{@team.name}
|
||||
|
||||
%fieldset
|
||||
%legend Members (#{@team.members.count})
|
||||
= form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do
|
||||
%table#members_list
|
||||
%thead
|
||||
%tr
|
||||
%th User name
|
||||
%th Default project access
|
||||
%th Team access
|
||||
%th
|
||||
- @team.members.each do |member|
|
||||
%tr.member
|
||||
%td
|
||||
= link_to [:admin, member] do
|
||||
= member.name
|
||||
%small= "(#{member.email})"
|
||||
%td= @team.human_default_projects_access(member)
|
||||
%td= @team.admin?(member) ? "Admin" : "Member"
|
||||
%td
|
||||
%tr
|
||||
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_email), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
|
||||
%td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
|
||||
%td
|
||||
%span= check_box_tag :group_admin
|
||||
%span Admin?
|
||||
%td= submit_tag 'Add', class: "btn primary", id: :add_members_to_team
|
19
app/views/admin/teams/new.html.haml
Normal file
19
app/views/admin/teams/new.html.haml
Normal file
|
@ -0,0 +1,19 @@
|
|||
%h3.page_title New Team
|
||||
%hr
|
||||
= form_for @team, url: admin_teams_path do |f|
|
||||
- if @team.errors.any?
|
||||
.alert-message.block-message.error
|
||||
%span= @team.errors.full_messages.first
|
||||
.clearfix
|
||||
= f.label :name do
|
||||
Team name is
|
||||
.input
|
||||
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
|
||||
|
||||
= f.submit 'Create team', class: "btn primary"
|
||||
%hr
|
||||
.padded
|
||||
%ul
|
||||
%li All created teams are public (users can view who enter into team and which project are assigned for this team)
|
||||
%li People within a team see only projects they have access to
|
||||
%li You will be able to assign existing projects for team
|
16
app/views/admin/teams/projects/_form.html.haml
Normal file
16
app/views/admin/teams/projects/_form.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
|||
= form_tag admin_team_project_path(@team, @project), method: :put do
|
||||
-if @project.errors.any?
|
||||
.alert-message.block-message.error
|
||||
%ul
|
||||
- @project.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
.clearfix
|
||||
%label Max access for Team members:
|
||||
.input
|
||||
= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles, @team.max_project_access(@project)), class: "project-access-select chosen span3"
|
||||
|
||||
%br
|
||||
.actions
|
||||
= submit_tag 'Save', class: "btn primary"
|
||||
= link_to 'Cancel', :back, class: "btn"
|
16
app/views/admin/teams/projects/edit.html.haml
Normal file
16
app/views/admin/teams/projects/edit.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
|||
%h3
|
||||
Edit max access in #{@project.name} for #{@team.name} team
|
||||
|
||||
%hr
|
||||
%table.zebra-striped
|
||||
%tr
|
||||
%td Project:
|
||||
%td= @project.name
|
||||
%tr
|
||||
%td Team:
|
||||
%td= @team.name
|
||||
%tr
|
||||
%td Since:
|
||||
%td= assigned_since(@team, @project).stamp("Nov 11, 2010")
|
||||
|
||||
= render 'form'
|
23
app/views/admin/teams/projects/new.html.haml
Normal file
23
app/views/admin/teams/projects/new.html.haml
Normal file
|
@ -0,0 +1,23 @@
|
|||
%h3.page_title
|
||||
Team: #{@team.name}
|
||||
|
||||
%fieldset
|
||||
%legend Projects (#{@team.projects.count})
|
||||
= form_tag admin_team_projects_path(@team), id: "assign_projects", class: "bulk_import", method: :post do
|
||||
%table#projects_list
|
||||
%thead
|
||||
%tr
|
||||
%th Project name
|
||||
%th Max access
|
||||
%th
|
||||
- @team.projects.each do |project|
|
||||
%tr.project
|
||||
%td
|
||||
= link_to project.name_with_namespace, [:admin, project]
|
||||
%td
|
||||
%span= @team.human_max_project_access(project)
|
||||
%td
|
||||
%tr
|
||||
%td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
|
||||
%td= select_tag :greatest_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
|
||||
%td= submit_tag 'Add', class: "btn primary", id: :assign_projects_to_team
|
101
app/views/admin/teams/show.html.haml
Normal file
101
app/views/admin/teams/show.html.haml
Normal file
|
@ -0,0 +1,101 @@
|
|||
%h3.page_title
|
||||
Team: #{@team.name}
|
||||
|
||||
%br
|
||||
%table.zebra-striped
|
||||
%thead
|
||||
%tr
|
||||
%th Team
|
||||
%th
|
||||
%tr
|
||||
%td
|
||||
%b
|
||||
Name:
|
||||
%td
|
||||
= @team.name
|
||||
|
||||
= link_to edit_admin_team_path(@team), class: "btn btn-small right" do
|
||||
%i.icon-edit
|
||||
Rename
|
||||
%tr
|
||||
%td
|
||||
%b
|
||||
Owner:
|
||||
%td
|
||||
= @team.owner.name
|
||||
.right
|
||||
= link_to "#", class: "btn btn-small change-owner-link" do
|
||||
%i.icon-edit
|
||||
Change owner
|
||||
|
||||
%tr.change-owner-holder.hide
|
||||
%td.bgred
|
||||
%b.cred
|
||||
New Owner:
|
||||
%td.bgred
|
||||
= form_for @team, url: admin_team_path(@team) do |f|
|
||||
= f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
|
||||
%div
|
||||
= f.submit 'Change Owner', class: "btn danger"
|
||||
= link_to "Cancel", "#", class: "btn change-owner-cancel-link"
|
||||
|
||||
%fieldset
|
||||
%legend
|
||||
Members (#{@team.members.count})
|
||||
%span= link_to 'Add members', new_admin_team_member_path(@team), class: "btn success small right", id: :add_members_to_team
|
||||
- if @team.members.any?
|
||||
%table#members_list
|
||||
%thead
|
||||
%tr
|
||||
%th User name
|
||||
%th Default project access
|
||||
%th Team access
|
||||
%th.cred.span3 Danger Zone!
|
||||
- @team.members.each do |member|
|
||||
%tr.member{ class: "user_#{member.id}"}
|
||||
%td
|
||||
= link_to [:admin, member] do
|
||||
= member.name
|
||||
%small= "(#{member.email})"
|
||||
%td= @team.human_default_projects_access(member)
|
||||
%td= @team.admin?(member) ? "Admin" : "Member"
|
||||
%td.bgred
|
||||
= link_to 'Edit', edit_admin_team_member_path(@team, member), class: "btn small"
|
||||
|
||||
= 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}"
|
||||
|
||||
%fieldset
|
||||
%legend
|
||||
Projects (#{@team.projects.count})
|
||||
%span= link_to 'Add projects', new_admin_team_project_path(@team), class: "btn success small right", id: :assign_projects_to_team
|
||||
- if @team.projects.any?
|
||||
%table#projects_list
|
||||
%thead
|
||||
%tr
|
||||
%th Project name
|
||||
%th Max access
|
||||
%th.cred.span3 Danger Zone!
|
||||
- @team.projects.each do |project|
|
||||
%tr.project
|
||||
%td
|
||||
= link_to project.name_with_namespace, [:admin, project]
|
||||
%td
|
||||
%span= @team.human_max_project_access(project)
|
||||
%td.bgred
|
||||
= link_to 'Edit', edit_admin_team_project_path(@team, project), class: "btn small"
|
||||
|
||||
= 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}"
|
||||
|
||||
:javascript
|
||||
$(function(){
|
||||
var modal = $('.change-owner-holder');
|
||||
$('.change-owner-link').bind("click", function(){
|
||||
$(this).hide();
|
||||
modal.show();
|
||||
});
|
||||
$('.change-owner-cancel-link').bind("click", function(){
|
||||
modal.hide();
|
||||
$('.change-owner-link').show();
|
||||
})
|
||||
})
|
||||
|
|
@ -46,6 +46,14 @@
|
|||
= f.label :projects_limit
|
||||
.input= f.number_field :projects_limit
|
||||
|
||||
.clearfix
|
||||
= f.label :can_create_group
|
||||
.input= f.check_box :can_create_group
|
||||
|
||||
.clearfix
|
||||
= f.label :can_create_team
|
||||
.input= f.check_box :can_create_team
|
||||
|
||||
.clearfix
|
||||
= f.label :admin do
|
||||
%strong.cred Administrator
|
||||
|
|
|
@ -123,5 +123,5 @@
|
|||
%tr
|
||||
%td= link_to project.name_with_namespace, admin_project_path(project)
|
||||
%td= tm.project_access_human
|
||||
%td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
|
||||
%td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
|
||||
%td= link_to 'Edit Access', edit_admin_project_member_path(project, tm.user), class: "btn small"
|
||||
%td= link_to 'Remove from team', admin_project_member_path(project, tm.user), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
|
||||
|
|
|
@ -11,19 +11,7 @@
|
|||
|
||||
:javascript
|
||||
$(function(){
|
||||
var w, h;
|
||||
$('.diff_file').each(function(){
|
||||
$('.image.diff_removed img', this).on('load', $.proxy(function(event){
|
||||
var w = event.currentTarget.naturalWidth
|
||||
, h = event.currentTarget.naturalHeight;
|
||||
$('.image.diff_removed .image-info', this).append(' | <b>W:</b> ' + w + 'px | <b>H:</b> ' + h + 'px');
|
||||
}, this));
|
||||
$('.image.diff_added img', this).on('load', $.proxy(function(event){
|
||||
var w = event.currentTarget.naturalWidth
|
||||
, h = event.currentTarget.naturalHeight;
|
||||
$('.image.diff_added .image-info', this).append(' | <b>W:</b> ' + w + 'px | <b>H:</b> ' + h + 'px');
|
||||
}, this));
|
||||
|
||||
$('.files .file').each(function(){
|
||||
new CommitFile(this);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
%div.ui-box
|
||||
%h5.title
|
||||
%i.icon-calendar
|
||||
= day.stamp("28 Aug, 2010")
|
||||
%span= day.stamp("28 Aug, 2010")
|
||||
%ul.well-list= render commits
|
||||
|
|
|
@ -12,50 +12,38 @@
|
|||
.file-stats
|
||||
= render "commits/diff_head", diffs: diffs
|
||||
|
||||
- unless @suppress_diff
|
||||
- diffs.each_with_index do |diff, i|
|
||||
- next if diff.diff.empty?
|
||||
- file = (@commit.tree / diff.new_path)
|
||||
- file = (@commit.prev_commit.tree / diff.old_path) unless file
|
||||
- next unless file
|
||||
.diff_file{id: "diff-#{i}"}
|
||||
.diff_file_header
|
||||
- if diff.deleted_file
|
||||
%span= diff.old_path
|
||||
.files
|
||||
- unless @suppress_diff
|
||||
- diffs.each_with_index do |diff, i|
|
||||
- next if diff.diff.empty?
|
||||
- file = (@commit.tree / diff.new_path)
|
||||
- file = (@commit.prev_commit.tree / diff.old_path) unless file
|
||||
- next unless file
|
||||
.file{id: "diff-#{i}"}
|
||||
.header
|
||||
- if diff.deleted_file
|
||||
%span= diff.old_path
|
||||
|
||||
- if @commit.prev_commit
|
||||
= link_to project_tree_path(@project, tree_join(@commit.prev_commit_id, diff.new_path)), {:class => 'btn right view-commit'} do
|
||||
- if @commit.prev_commit
|
||||
= link_to project_tree_path(@project, tree_join(@commit.prev_commit_id, diff.new_path)), {:class => 'btn right view-file'} do
|
||||
View file @
|
||||
%span.commit-short-id= @commit.short_id(6)
|
||||
- else
|
||||
%span= diff.new_path
|
||||
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
|
||||
%span.file-mode= "#{diff.a_mode} → #{diff.b_mode}"
|
||||
|
||||
= link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn very_small right view-file'} do
|
||||
View file @
|
||||
%span.commit-short-id= @commit.short_id(6)
|
||||
- else
|
||||
%span= diff.new_path
|
||||
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
|
||||
%span.file-mode= "#{diff.a_mode} → #{diff.b_mode}"
|
||||
|
||||
= link_to project_tree_path(@project, tree_join(@commit.id, diff.new_path)), {:class => 'btn very_small right view-commit'} do
|
||||
View file @
|
||||
%span.commit-short-id= @commit.short_id(6)
|
||||
|
||||
%br/
|
||||
.diff_file_content
|
||||
-# Skip all non-supported blobs
|
||||
- next unless file.respond_to?('text?')
|
||||
- if file.text?
|
||||
= render "commits/text_diff", diff: diff, index: i
|
||||
- elsif file.image?
|
||||
- old_file = (@commit.prev_commit.tree / diff.old_path) if !@commit.prev_commit.nil?
|
||||
- if diff.renamed_file || diff.new_file || diff.deleted_file
|
||||
.diff_file_content_image
|
||||
.image{class: image_diff_class(diff)}
|
||||
%img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
|
||||
%div.image-info= "#{number_to_human_size file.size}"
|
||||
.content
|
||||
-# Skipp all non non-supported blobs
|
||||
- next unless file.respond_to?('text?')
|
||||
- if file.text?
|
||||
= render "commits/text_file", diff: diff, index: i
|
||||
- elsif file.image?
|
||||
- old_file = (@commit.prev_commit.tree / diff.old_path) if !@commit.prev_commit.nil?
|
||||
= render "commits/image", diff: diff, old_file: old_file, file: file, index: i
|
||||
- else
|
||||
.diff_file_content_image.img_compared
|
||||
.image.diff_removed
|
||||
%img{src: "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
|
||||
%div.image-info= "#{number_to_human_size file.size}"
|
||||
.image.diff_added
|
||||
%img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
|
||||
%div.image-info= "#{number_to_human_size file.size}"
|
||||
- else
|
||||
%p.nothing_here_message No preview for this file type
|
||||
%p.nothing_here_message No preview for this file type
|
||||
|
|
63
app/views/commits/_image.html.haml
Normal file
63
app/views/commits/_image.html.haml
Normal file
|
@ -0,0 +1,63 @@
|
|||
- if diff.renamed_file || diff.new_file || diff.deleted_file
|
||||
.image
|
||||
%span.wrap
|
||||
.frame{class: image_diff_class(diff)}
|
||||
%img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
|
||||
%p.image-info= "#{number_to_human_size file.size}"
|
||||
- else
|
||||
.image
|
||||
%div.two-up.view
|
||||
%span.wrap
|
||||
.frame.deleted
|
||||
%a{href: project_tree_path(@project, tree_join(@commit.id, diff.old_path))}
|
||||
%img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
|
||||
%p.image-info.hide
|
||||
%span.meta-filesize= "#{number_to_human_size old_file.size}"
|
||||
|
|
||||
%b W:
|
||||
%span.meta-width
|
||||
|
|
||||
%b H:
|
||||
%span.meta-height
|
||||
%span.wrap
|
||||
.frame.added
|
||||
%a{href: project_tree_path(@project, tree_join(@commit.id, diff.new_path))}
|
||||
%img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
|
||||
%p.image-info.hide
|
||||
%span.meta-filesize= "#{number_to_human_size file.size}"
|
||||
|
|
||||
%b W:
|
||||
%span.meta-width
|
||||
|
|
||||
%b H:
|
||||
%span.meta-height
|
||||
|
||||
%div.swipe.view.hide
|
||||
.swipe-frame
|
||||
.frame.deleted
|
||||
%img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
|
||||
.swipe-wrap
|
||||
.frame.added
|
||||
%img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
|
||||
%span.swipe-bar
|
||||
%span.top-handle
|
||||
%span.bottom-handle
|
||||
|
||||
%div.onion-skin.view.hide
|
||||
.onion-skin-frame
|
||||
.frame.deleted
|
||||
%img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
|
||||
.frame.added
|
||||
%img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
|
||||
.controls
|
||||
.transparent
|
||||
.drag-track
|
||||
.dragger{:style => "left: 0px;"}
|
||||
.opaque
|
||||
|
||||
|
||||
.view-modes.hide
|
||||
%ul.view-modes-menu
|
||||
%li.two-up{data: {mode: 'two-up'}} 2-up
|
||||
%li.swipe{data: {mode: 'swipe'}} Swipe
|
||||
%li.onion-skin{data: {mode: 'onion-skin'}} Onion skin
|
|
@ -2,7 +2,7 @@
|
|||
- if too_big
|
||||
%a.supp_diff_link Diff suppressed. Click to show
|
||||
|
||||
%table{class: "#{'hide' if too_big}"}
|
||||
%table.text-file{class: "#{'hide' if too_big}"}
|
||||
- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old|
|
||||
%tr.line_holder{ id: line_code }
|
||||
- if type == "match"
|
|
@ -5,7 +5,7 @@
|
|||
= breadcrumbs
|
||||
|
||||
%div{id: dom_id(@project)}
|
||||
#commits_list= render "commits"
|
||||
#commits-list= render "commits"
|
||||
.clear
|
||||
.loading{ style: "display:none;"}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
.groups_box
|
||||
.ui-box
|
||||
%h5.title
|
||||
Groups
|
||||
%small
|
||||
(#{groups.count})
|
||||
- if current_user.can_create_group?
|
||||
%span.right
|
||||
= link_to new_admin_group_path, class: "btn very_small info" do
|
||||
= link_to new_group_path, class: "btn very_small info" do
|
||||
%i.icon-plus
|
||||
New Group
|
||||
%ul.well-list
|
||||
|
@ -13,8 +13,6 @@
|
|||
%li
|
||||
= link_to group_path(id: group.path), class: dom_class(group) do
|
||||
%strong.well-title= truncate(group.name, length: 35)
|
||||
%span.arrow
|
||||
→
|
||||
%span.last_activity
|
||||
%strong Projects:
|
||||
%span= current_user.authorized_projects.where(namespace_id: group.id).count
|
||||
%span.right.light
|
||||
- if group.owner == current_user
|
||||
%i.icon-wrench
|
||||
|
|
|
@ -2,19 +2,12 @@
|
|||
%h5.title
|
||||
Projects
|
||||
%small
|
||||
(#{projects.total_count})
|
||||
(#{@projects_count})
|
||||
- if current_user.can_create_project?
|
||||
%span.right
|
||||
= link_to new_project_path, class: "btn very_small info" do
|
||||
%i.icon-plus
|
||||
New Project
|
||||
%ul.nav.nav-projects-tabs
|
||||
= nav_tab :scope, nil do
|
||||
= link_to "All", dashboard_path
|
||||
= nav_tab :scope, 'personal' do
|
||||
= link_to "Personal", dashboard_path(scope: 'personal')
|
||||
= nav_tab :scope, 'joined' do
|
||||
= link_to "Joined", dashboard_path(scope: 'joined')
|
||||
|
||||
%ul.well-list
|
||||
- projects.each do |project|
|
||||
|
@ -33,4 +26,6 @@
|
|||
- if projects.blank?
|
||||
%li
|
||||
%h3.nothing_here_message There are no projects here.
|
||||
.bottom= paginate projects, theme: "gitlab"
|
||||
- if @projects_count > 20
|
||||
%li.bottom
|
||||
%strong= link_to "show all projects", projects_dashboard_path
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
- if @teams.present?
|
||||
= render "teams", teams: @teams
|
||||
- if @groups.present?
|
||||
= render "groups", groups: @groups
|
||||
= render "projects", projects: @projects
|
||||
|
|
20
app/views/dashboard/_teams.html.haml
Normal file
20
app/views/dashboard/_teams.html.haml
Normal file
|
@ -0,0 +1,20 @@
|
|||
.ui-box.teams-box
|
||||
%h5.title
|
||||
Teams
|
||||
%small
|
||||
(#{@teams.count})
|
||||
%span.right
|
||||
= link_to new_team_path, class: "btn very_small info" do
|
||||
%i.icon-plus
|
||||
New Team
|
||||
%ul.well-list
|
||||
- @teams.each do |team|
|
||||
%li
|
||||
= link_to team_path(id: team.path), class: dom_class(team) do
|
||||
%strong.well-title= truncate(team.name, length: 35)
|
||||
%span.right.light
|
||||
- if team.owner == current_user
|
||||
%i.icon-wrench
|
||||
- tm = current_user.user_team_user_relationships.find_by_user_team_id(team.id)
|
||||
- if tm
|
||||
= tm.access_human
|
|
@ -1,9 +1,9 @@
|
|||
xml.instruct!
|
||||
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
|
||||
xml.title "#{current_user.name} issues"
|
||||
xml.link :href => dashboard_issues_url(:atom, :private_token => current_user.private_token), :rel => "self", :type => "application/atom+xml"
|
||||
xml.link :href => dashboard_issues_url(:private_token => current_user.private_token), :rel => "alternate", :type => "text/html"
|
||||
xml.id dashboard_issues_url(:private_token => current_user.private_token)
|
||||
xml.link :href => issues_dashboard_url(:atom, :private_token => current_user.private_token), :rel => "self", :type => "application/atom+xml"
|
||||
xml.link :href => issues_dashboard_url(:private_token => current_user.private_token), :rel => "alternate", :type => "text/html"
|
||||
xml.id issues_dashboard_url(:private_token => current_user.private_token)
|
||||
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
|
||||
|
||||
@issues.each do |issue|
|
||||
|
|
56
app/views/dashboard/projects.html.haml
Normal file
56
app/views/dashboard/projects.html.haml
Normal file
|
@ -0,0 +1,56 @@
|
|||
%h3.page_title
|
||||
Projects
|
||||
%span
|
||||
(#{@projects.total_count})
|
||||
- if current_user.can_create_project?
|
||||
%span.right
|
||||
= link_to new_project_path, class: "btn very_small info" do
|
||||
%i.icon-plus
|
||||
New Project
|
||||
|
||||
|
||||
%hr
|
||||
.row
|
||||
.span3
|
||||
%ul.nav.nav-pills.nav-stacked
|
||||
= nav_tab :scope, nil do
|
||||
= link_to "All", projects_dashboard_path
|
||||
= nav_tab :scope, 'personal' do
|
||||
= link_to "Personal", projects_dashboard_path(scope: 'personal')
|
||||
= nav_tab :scope, 'joined' do
|
||||
= link_to "Joined", projects_dashboard_path(scope: 'joined')
|
||||
|
||||
.span9
|
||||
= form_tag projects_dashboard_path, method: 'get' do
|
||||
%fieldset.dashboard-search-filter
|
||||
= hidden_field_tag "scope", params[:scope]
|
||||
= search_field_tag "search", params[:search], { placeholder: 'Search', class: 'left input-xxlarge' }
|
||||
= button_tag type: 'submit', class: 'btn' do
|
||||
%i.icon-search
|
||||
|
||||
%ul.well-list
|
||||
- @projects.each do |project|
|
||||
%li.clearfix
|
||||
.left
|
||||
= link_to project_path(project), class: dom_class(project) do
|
||||
- if project.namespace
|
||||
= project.namespace.human_name
|
||||
\/
|
||||
%strong.well-title
|
||||
= truncate(project.name, length: 25)
|
||||
%br
|
||||
%small.light
|
||||
%strong Last activity:
|
||||
%span= project_last_activity(project)
|
||||
.right.light
|
||||
- if project.owner == current_user
|
||||
%i.icon-wrench
|
||||
- tm = project.team.get_tm(current_user.id)
|
||||
- if tm
|
||||
= tm.project_access_human
|
||||
|
||||
- if @projects.blank?
|
||||
%li
|
||||
%h3.nothing_here_message There are no projects here.
|
||||
.bottom= paginate @projects, theme: "gitlab"
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
xml.instruct!
|
||||
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
|
||||
xml.title "#{@user.name} issues"
|
||||
xml.link :href => dashboard_issues_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml"
|
||||
xml.link :href => dashboard_issues_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html"
|
||||
xml.id dashboard_issues_url(:private_token => @user.private_token)
|
||||
xml.link :href => issues_dashboard_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml"
|
||||
xml.link :href => issues_dashboard_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html"
|
||||
xml.id issues_dashboard_url(:private_token => @user.private_token)
|
||||
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
|
||||
|
||||
@issues.each do |issue|
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue