Commit 66ebf8d8 authored by Lennart Rosam's avatar Lennart Rosam

Merge remote-tracking branch 'github/master'

parents dc13af90 bd948549
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
class CommitFile
constructor: (file) ->
if $('.image', file).length
new ImageFile(file)
this.CommitFile = CommitFile
\ No newline at end of file
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
\ No newline at end of file
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();
}
});
}
}
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
\ No newline at end of file
......@@ -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(",")
#
# * 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'))
......
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();
}
});
}
}
@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()
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;
}
/**
......
/** Colors **/
/**
* General Colors
*/
$primary_color: #2FA0BB;
$link_color: #3A89A3;
$style_color: #474D57;
$hover: #D9EDF7;
/**
* Commit Diff Colors
*/
$added: #63c363;
$deleted: #f77;
/**
*
* 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;
font-size: 12px;
}
}
.old_line, .new_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;
@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;
}
}
}
.diff_file_content_image {
background: #eee;
.image {
background: #ddd;
text-align: center;
.image {
padding: 30px;
.wrap{
display: inline-block;
margin: 50px;
max-width: 400px;
}
.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;
}
&.diff_removed {
img{
border: 1px solid #C00;
}
&.added {
border: 1px solid $added;
}
}
.image-info{
font-size: 12px;
margin: 5px 0 0 0;
color: grey;
}
&.diff_added {
img{
border: 1px solid #0C0;
.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;
}
}
.image-info{
margin: 5px 0 0 0;
.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;
}
}
}
&.img_compared {
.image {
max-width: 300px;
} //.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{
.diff_file_content{
table {
border: none;
margin: 0px;
padding: 0px;
tr {
td {
font-size: 12px;
}
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;
}
}
.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;
color: #666;
&:hover {
li{
color: grey;
border-left: 1px solid #c1c1c1;
padding: 0 12px 0 16px;
cursor: pointer;
&:first-child{
border-left: none;
}
&: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;
&.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;
......
# 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!
......
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)
......
class Admin::GroupsController < AdminController
class Admin::GroupsController < Admin::ApplicationController
before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update]
def index
......
class Admin::HooksController < AdminController
class Admin::HooksController < Admin::ApplicationController
def index
@hooks = SystemHook.all
@hook = SystemHook.new
......
class Admin::LogsController < AdminController
class Admin::LogsController < Admin::ApplicationController
end
# 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
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
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.'
......
class Admin::ResqueController < AdminController
class Admin::ResqueController < Admin::ApplicationController
def show
end
end
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
# 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
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
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
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
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
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
......
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
class Projects::ApplicationController < ApplicationController
before_filter :authorize_admin_team_member!
protected
def user_team
@team ||= UserTeam.find_by_path(params[:id])
end
end
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
......
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
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
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
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
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
module Admin::Teams::MembersHelper
def member_since(team, member)
team.user_team_user_relationships.find_by_user_id(member).created_at
end
end
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)
......
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
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)
......
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_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_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, 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
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
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
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"}
......
= form_for @admin_team_member, as: :team_member, url: admin_team_member_path(@admin_team_member) do |f|
-if @admin_team_member.errors.any?
= 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
- @admin_team_member.errors.full_messages.each do |msg|
- @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, @admin_team_member.project_access), {}, class: "project-access-select chosen span3"
= f.select :project_access, options_for_select(Project.access_options, @team_member_relation.project_access), {}, class: "project-access-select chosen span3"
%br
.actions
......
%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
......
%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'
%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"
%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"
= 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"
%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'
%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
%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"
&nbsp;
= 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
= 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"
%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'
%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
%h3.page_title
Team: #{@team.name}
%br
%table.zebra-striped
%thead
%tr
%th Team
%th
%tr
%td
%b
Name:
%td
= @team.name
&nbsp;
= 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"
&nbsp;
= 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"
&nbsp;
= 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
- 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
\ No newline at end of file
......@@ -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;"}
......
.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
&rarr;
%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
- if @teams.present?
= render "teams", teams: @teams
- if @groups.present?
= render "groups", groups: @groups
= render "projects", projects: @projects
......
.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
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|
......
%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"
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|
......
%h3.page_title New Group
%hr
= form_for @group do |f|
- if @group.errors.any?
.alert-message.block-message.error
%span= @group.errors.full_messages.first
.clearfix
= f.label :name do
Group name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
&nbsp;
= f.submit 'Create group', class: "btn primary"
%hr
.padded
%ul
%li Group is kind of directory for several projects
%li All created groups are private
%li People within a group see only projects they have access to
%li All projects of group will be stored in group directory
%li You will be able to move existing projects into group
......@@ -17,7 +17,7 @@
= link_to new_project_path, title: "Create New Project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project' do
%i.icon-plus
%li
= link_to profile_path, title: "Your Profile", class: 'has_bottom_tooltip', 'data-original-title' => 'Your profile' do
= link_to profile_path, title: "My Profile", class: 'has_bottom_tooltip', 'data-original-title' => 'Your profile' do
%i.icon-user
%li.separator
%li
......
.search
= form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f|
= text_field_tag "search", nil, placeholder: "Search", class: "search-input"
= hidden_field_tag :group_id, @group.try(:id)
= hidden_field_tag :project_id, @project.try(:id)
:javascript
$(function(){
......
......@@ -10,6 +10,8 @@
= link_to "Stats", admin_root_path
= nav_link(controller: :projects) do
= link_to "Projects", admin_projects_path
= nav_link(controller: :teams) do
= link_to "Teams", admin_teams_path
= nav_link(controller: :groups) do
= link_to "Groups", admin_groups_path
= nav_link(controller: :users) do
......
......@@ -6,14 +6,17 @@
= render "layouts/head_panel", title: "Dashboard"
.container
%ul.main_menu
= nav_link(path: 'dashboard#index', html_options: {class: 'home'}) do
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
= link_to "Home", root_path, title: "Home"
= nav_link(path: 'dashboard#projects') do
= link_to projects_dashboard_path do
Projects
= nav_link(path: 'dashboard#issues') do
= link_to dashboard_issues_path do
= link_to issues_dashboard_path do
Issues
%span.count= current_user.assigned_issues.opened.count
= nav_link(path: 'dashboard#merge_requests') do
= link_to dashboard_merge_requests_path do
= link_to merge_requests_dashboard_path do
Merge Requests
%span.count= current_user.cared_merge_requests.opened.count
= nav_link(path: 'search#show') do
......
......@@ -3,7 +3,7 @@
= render "layouts/head", title: "#{@group.name}"
%body{class: "#{app_theme} application"}
= render "layouts/flash"
= render "layouts/head_panel", title: "#{@group.name}"
= render "layouts/head_panel", title: "group: #{@group.name}"
.container
%ul.main_menu
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
......
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "#{@team.name}"
%body{class: "#{app_theme} application"}
= render "layouts/flash"
= render "layouts/head_panel", title: "team: #{@team.name}"
.container
%ul.main_menu
= nav_link(path: 'teams#show', html_options: {class: 'home'}) do
= link_to "Home", team_path(@team), title: "Home"
= nav_link(path: 'teams#issues') do
= link_to issues_team_path(@team) do
Issues
%span.count= Issue.opened.of_user_team(@team).count
= nav_link(path: 'teams#merge_requests') do
= link_to merge_requests_team_path(@team) do
Merge Requests
%span.count= MergeRequest.opened.of_user_team(@team).count
= nav_link(controller: [:members]) do
= link_to team_members_path(@team), class: "team-tab tab" do
Members
%span.count= @team.members.count
- if can? current_user, :admin_user_team, @team
= nav_link(controller: [:projects]) do
= link_to team_projects_path(@team), class: "team-tab tab" do
Projects
%span.count= @team.projects.count
= nav_link(path: 'teams#edit') do
= link_to edit_team_path(@team), class: "stat-tab tab " do
%i.icon-edit
Edit Team
.content= yield
......@@ -38,7 +38,7 @@
- if note.for_diff_line?
- if note.diff
.content
.diff_file= render "notes/discussion_diff", discussion_notes: discussion_notes, note: note
.file= render "notes/discussion_diff", discussion_notes: discussion_notes, note: note
- else
= link_to 'show outdated discussion', '#', class: 'js-show-outdated-discussion'
%div.hide.outdated-discussion
......
- diff = note.diff
.diff_file_header
.header
- if diff.deleted_file
%span= diff.old_path
- else
......@@ -7,7 +7,7 @@
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
%br/
.diff_file_content
.content
%table
- each_diff_line(diff, note.diff_file_index) do |line, type, line_code, line_new, line_old|
%tr.line_holder{ id: line_code }
......
......@@ -31,6 +31,20 @@
.controls
= f.text_field :email, class: "input-xlarge", required: true
%span.help-block We also use email for avatar detection.
.control-group
= f.label :skype, class: "control-label"
.controls= f.text_field :skype, class: "input-xlarge"
.control-group
= f.label :linkedin, class: "control-label"
.controls= f.text_field :linkedin, class: "input-xlarge"
.control-group
= f.label :twitter, class: "control-label"
.controls= f.text_field :twitter, class: "input-xlarge"
.control-group
= f.label :bio, class: "control-label"
.controls
= f.text_area :bio, rows: 6, class: "input-xlarge", maxlength: 250
%span.help-block Tell us about yourself in fewer than 250 characters.
.span5.right
%fieldset.tips
......@@ -47,24 +61,18 @@
%p
You can login through #{@user.provider.titleize}!
= link_to "click here to change", account_profile_path
.row
.span7
.control-group
= f.label :skype, class: "control-label"
.controls= f.text_field :skype, class: "input-xlarge"
.control-group
= f.label :linkedin, class: "control-label"
.controls= f.text_field :linkedin, class: "input-xlarge"
.control-group
= f.label :twitter, class: "control-label"
.controls= f.text_field :twitter, class: "input-xlarge"
.control-group
= f.label :bio, class: "control-label"
.controls
= f.text_area :bio, rows: 6, class: "input-xlarge", maxlength: 250
%span.help-block Tell us about yourself in fewer than 250 characters.
.span5.right
- if current_user.can_create_group?
%li
%p
Need a group for several dependent projects?
= link_to new_group_path, class: "btn very_small" do
Create a group
- if current_user.can_create_team?
%li
%p
Want to share a team between projects?
= link_to new_team_path, class: "btn very_small" do
Create a team
%fieldset
%legend
Personal projects:
......@@ -79,7 +87,8 @@
%fieldset
%legend
SSH public keys:
%strong.right= link_to current_user.keys.count, keys_path
%span.right
= link_to pluralize(current_user.keys.count, 'key'), keys_path
.padded
= link_to "Add Public Key", new_key_path, class: "btn small"
......
......@@ -15,6 +15,20 @@
%span Namespace
.input
= f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'chosen'}
%hr
%p.padded
New projects are private by default. You choose who can see the project and commit to repository.
%hr
- if current_user.can_create_group?
.clearfix
.input.light
Need a group for several dependent projects?
= link_to new_group_path, class: "btn very_small" do
Create a group
- if current_user.can_create_team?
.clearfix
.input.light
Want to share a project between team?
= link_to new_team_path, class: "btn very_small" do
Create a team
......@@ -3,7 +3,7 @@
= link_to project_path(@project), class: "activities-tab tab" do
%i.icon-home
Show
= nav_link(controller: :team_members) do
= nav_link(controller: [:team_members, :teams]) do
= link_to project_team_index_path(@project), class: "team-tab tab" do
%i.icon-user
Team
......
%h3.page_title Project Network Graph
%br
.graph_holder
%h4
%small You can move around the graph by using the arrow keys.
......@@ -11,6 +12,6 @@
$(function(){
branch_graph = new BranchGraph($("#holder"), {
url: '#{url_for controller: 'projects', action: 'graph', format: :json}',
commit_url: '#{url_for controller: 'projects', action: 'show'}/commits/%s'
commit_url: '#{project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")}'
});
});
= render "projects/project_head"
%h3.page_title
= "Assign project to team of users"
%hr
%p.slead
Read more about assign to team of users #{link_to "here", '#', class: 'vlink'}.
= form_tag assign_project_teams_path(@project), method: 'post' do
%p.slead Choose Team of users you want to assign:
.padded
= label_tag :team_id, "Team"
.input= select_tag(:team_id, options_from_collection_for_select(@teams, :id, :name), prompt: "Select team", class: "chosen xxlarge", required: true)
%p.slead Choose greatest user acces in team you want to assign:
.padded
= label_tag :team_ids, "Permission"
.input= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles), {class: "project-access-select chosen span3" }
.actions
= submit_tag 'Assign', class: "btn save-btn"
= link_to "Cancel", project_team_index_path(@project), class: "btn cancel-btn"
%fieldset
%legend Groups:
%ul.nav.nav-pills.nav-stacked
%li{class: ("active" if params[:group_id].blank?)}
= link_to search_path(group_id: nil, search: params[:search]) do
Any
- current_user.authorized_groups.each do |group|
%li{class: ("active" if params[:group_id] == group.id.to_s)}
= link_to search_path(group_id: group.id, search: params[:search]) do
= group.name
%fieldset
%legend Projects:
%ul.nav.nav-pills.nav-stacked
%li{class: ("active" if params[:project_id].blank?)}
= link_to search_path(project_id: nil, search: params[:search]) do
Any
- current_user.authorized_projects.each do |project|
%li{class: ("active" if params[:project_id] == project.id.to_s)}
= link_to search_path(project_id: project.id, search: params[:search]) do
= project.name_with_namespace
= hidden_field_tag :group_id, params[:group_id]
= hidden_field_tag :project_id, params[:project_id]
%br
%h3.page_title
Search results
%span.cgray (#{@projects.count + @merge_requests.count + @issues.count + @wiki_pages.count})
%hr
%fieldset
%legend
Search results
%span.cgray (#{@projects.count + @merge_requests.count + @issues.count + @wiki_pages.count})
.search_results
.row
.span6
%table
%thead
%tr
%th Projects
%tbody
- @projects.each do |project|
%tr
%td
= link_to project do
%strong.term= project.name_with_namespace
%small.cgray
last activity at
= project.last_activity_date.stamp("Aug 25, 2011")
- if @projects.blank?
%tr
%td
%h4.nothing_here_message No Projects
%br
%table
%thead
%tr
%th Merge Requests
%tbody
- @merge_requests.each do |merge_request|
%tr
%td
= link_to [merge_request.project, merge_request] do
%span.badge.badge-info ##{merge_request.id}
&ndash;
%strong.term= truncate merge_request.title, length: 50
%strong.right
%span.label= merge_request.project.name
- if @merge_requests.blank?
%tr
%td
%h4.nothing_here_message No Merge Requests
.span6
%table
%thead
%tr
%th Issues
%tbody
- @issues.each do |issue|
%tr
%td
= link_to [issue.project, issue] do
%span.badge.badge-info ##{issue.id}
&ndash;
%strong.term= truncate issue.title, length: 40
%strong.right
%span.label= issue.project.name
- if @issues.blank?
%tr
%td
%h4.nothing_here_message No Issues
.span6
%table
%thead
%tr
%th Wiki
%tbody
- @wiki_pages.each do |wiki_page|
%tr
%td
= link_to project_wiki_path(wiki_page.project, wiki_page) do
%strong.term= truncate wiki_page.title, length: 40
%strong.right
%span.label= wiki_page.project.name
- if @wiki_pages.blank?
%tr
%td
%h4.nothing_here_message No wiki pages
%ul.well-list
- @projects.each do |project|
%li
project:
= link_to project do
%strong.term= project.name_with_namespace
- @merge_requests.each do |merge_request|
%li
merge request:
= link_to [merge_request.project, merge_request] do
%span ##{merge_request.id}
%strong.term
= truncate merge_request.title, length: 50
%span.light (#{merge_request.project.name_with_namespace})
- @issues.each do |issue|
%li
issue:
= link_to [issue.project, issue] do
%span ##{issue.id}
%strong.term
= truncate issue.title, length: 50
%span.light (#{issue.project.name_with_namespace})
- @wiki_pages.each do |wiki_page|
%li
wiki:
= link_to project_wiki_path(wiki_page.project, wiki_page) do
%strong.term
= truncate wiki_page.title, length: 50
%span.light (#{wiki_page.project.name_with_namespace})
:javascript
$(function() {
$(".search_results .term").highlight("#{escape_javascript(params[:search])}");
......
= form_tag search_path, method: :get, class: 'form-inline' do |f|
.padded
.search-holder
= label_tag :search do
%strong Looking for
%span Looking for
.input
= search_field_tag :search, params[:search], placeholder: "issue 143", class: "input-xxlarge search-text-input", id: "dashboard_search"
= submit_tag 'Search', class: "btn primary wide"
- if params[:search].present?
= render 'search/result'
.clearfix
.row
.span3
= render 'filter', f: f
.span9
.results
- if params[:search].present?
= render 'search/result'
%h3.page_title
= "New Team member(s)"
%hr
= form_for @team_member, as: :team_member, url: project_team_members_path(@project, @team_member) do |f|
-if @team_member.errors.any?
= form_for @user_project_relation, as: :team_member, url: project_team_members_path(@project) do |f|
-if @user_project_relation.errors.any?
.alert-message.block-message.error
%ul
- @team_member.errors.full_messages.each do |msg|
- @user_project_relation.errors.full_messages.each do |msg|
%li= msg
%h6 1. Choose people you want in the team
......@@ -16,7 +16,7 @@
%h6 2. Set access level for them
.clearfix
= f.label :project_access, "Project Access"
.input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select chosen"
.input= select_tag :project_access, options_for_select(Project.access_options, @user_project_relation.project_access), class: "project-access-select chosen"
.actions
= f.submit 'Save', class: "btn save-btn"
......
- user = member.user
- allow_admin = can? current_user, :admin_project, @project
%li{id: dom_id(member), class: "team_member_row user_#{user.id}"}
%li{id: dom_id(user), class: "team_member_row user_#{user.id}"}
.row
.span6
= link_to project_team_member_path(@project, member), title: user.name, class: "dark" do
= link_to project_team_member_path(@project, user), title: user.name, class: "dark" do
= image_tag gravatar_icon(user.email, 40), class: "avatar s32"
= link_to project_team_member_path(@project, member), title: user.name, class: "dark" do
= link_to project_team_member_path(@project, user), title: user.name, class: "dark" do
%strong= truncate(user.name, lenght: 40)
%br
%small.cgray= user.email
......@@ -13,7 +13,7 @@
.span5.right
- if allow_admin
.left
= form_for(member, as: :team_member, url: project_team_member_path(@project, member)) do |f|
= form_for(member, as: :team_member, url: project_team_member_path(@project, member.user)) do |f|
= f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2"
.right
- if current_user == user
......@@ -23,6 +23,6 @@
- elsif user.blocked
%span.btn.disabled.blocked Blocked
- elsif allow_admin
= link_to project_team_member_path(project_id: @project, id: member.id), confirm: remove_from_team_message(@project, member), method: :delete, class: "very_small btn danger" do
= link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "very_small btn danger" do
%i.icon-minus.icon-white
- team = team_rel.user_team
- allow_admin = can? current_user, :admin_team_member, @project
%li{id: dom_id(team), class: "user_team_row team_#{team.id}"}
.row
.span6
%strong= link_to team.name, team_path(team), title: team.name, class: "dark"
%br
%small.cgray Members: #{team.members.count}
.span5.right
.right
- if allow_admin
.left
= link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you shure?", class: "btn danger small" do
%i.icon-minus.icon-white
- grouper_project_teams(@project).each do |access, teams|
.ui-box
%h5.title
= UserTeam.access_roles.key(access).pluralize
%small= teams.size
%ul.well-list
- teams.sort_by(&:team_name).each do |tofr|
= render(partial: 'team_members/show_team', locals: {team_rel: tofr})
:javascript
$(function(){
$('.repo-access-select, .project-access-select').live("change", function() {
$(this.form).submit();
});
})
- if @team_member.valid?
- if @user_project_relation.valid?
:plain
$("#new_team_member").hide("slide", { direction: "right" }, 150, function(){
$("#team-table").show("slide", { direction: "left" }, 150, function() {
......
......@@ -4,7 +4,7 @@
= "Import team from another project"
%hr
%p.slead
Read more about team import #{link_to "here", '#', class: 'vlink'}.
Read more about project team import #{link_to "here", '#', class: 'vlink'}.
= form_tag apply_import_project_team_members_path(@project), method: 'post' do
%p.slead Choose project you want to use as team source:
.padded
......
= render "projects/project_head"
%h3.page_title
Team Members
(#{@project.users_projects.count})
(#{@project.users.count})
%small
Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink"
......@@ -10,11 +10,24 @@
%span.right
= link_to import_project_team_members_path(@project), class: "btn small grouped", title: "Import team from another project" do
Import team from another project
= link_to available_project_teams_path(@project), class: "btn small grouped", title: "Assign project to team of users" do
Assign project to Team of users
= link_to new_project_team_member_path(@project), class: "btn success small grouped", title: "New Team Member" do
New Team Member
%hr
%hr
.clearfix
%div.team-table
= render partial: "team_members/team", locals: {project: @project}
%h3.page_title
Assigned teams
(#{@project.user_teams.count})
%hr
.clearfix
%div.team-table
= render partial: "team_members/teams", locals: {project: @project}
- allow_admin = can? current_user, :admin_project, @project
- user = @team_member.user
.team_member_show
- if can? current_user, :admin_project, @project
= link_to 'Remove from team', project_team_member_path(project_id: @project, id: @team_member.id), confirm: 'Are you sure?', method: :delete, class: "right btn danger"
= link_to 'Remove from team', project_team_member_path(@project, @member), confirm: 'Are you sure?', method: :delete, class: "right btn danger"
.profile_avatar_holder
= image_tag gravatar_icon(user.email, 60), class: "borders"
= image_tag gravatar_icon(@member.email, 60), class: "borders"
%h3.page_title
= user.name
%small (@#{user.username})
= @member.name
%small (@#{@member.username})
%hr
.back_link
......@@ -21,34 +20,34 @@
%table.lite
%tr
%td Email
%td= mail_to user.email
%td= mail_to @member.email
%tr
%td Skype
%td= user.skype
- unless user.linkedin.blank?
%td= @member.skype
- unless @member.linkedin.blank?
%tr
%td LinkedIn
%td= user.linkedin
- unless user.twitter.blank?
%td= @member.linkedin
- unless @member.twitter.blank?
%tr
%td Twitter
%td= user.twitter
- unless user.bio.blank?
%td= @member.twitter
- unless @member.bio.blank?
%tr
%td Bio
%td= user.bio
%td= @member.bio
.span6
%table.lite
%tr
%td Member since
%td= @team_member.created_at.stamp("Aug 21, 2011")
%td= @user_project_relation.created_at.stamp("Aug 21, 2011")
%tr
%td
Project Access:
%small (#{link_to "read more", help_permissions_path, class: "vlink"})
%td
= form_for(@team_member, as: :team_member, url: project_team_member_path(@project, @team_member)) do |f|
= f.select :project_access, options_for_select(Project.access_options, @team_member.project_access), {}, class: "project-access-select", disabled: !allow_admin
= form_for(@user_project_relation, as: :team_member, url: project_team_member_path(@project, @member)) do |f|
= f.select :project_access, options_for_select(Project.access_options, @user_project_relation.project_access), {}, class: "project-access-select", disabled: !allow_admin
%hr
= render @events
:javascript
......
- if @team_member.valid?
- if @user_project_relation.valid?
:plain
$("##{dom_id(@team_member)}").effect("highlight", {color: "#529214"}, 1000);;
$("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#529214"}, 1000);;
- else
:plain
$("##{dom_id(@team_member)}").effect("highlight", {color: "#D12F19"}, 1000);;
$("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#D12F19"}, 1000);;
= form_tag team_filter_path(entity), method: 'get' do
%fieldset.dashboard-search-filter
= search_field_tag "search", params[:search], { placeholder: 'Search', class: 'search-text-input' }
= button_tag type: 'submit', class: 'btn' do
%i.icon-search
%fieldset
%legend Status:
%ul.nav.nav-pills.nav-stacked
%li{class: ("active" if !params[:status])}
= link_to team_filter_path(entity, status: nil) do
Open
%li{class: ("active" if params[:status] == 'closed')}
= link_to team_filter_path(entity, status: 'closed') do
Closed
%li{class: ("active" if params[:status] == 'all')}
= link_to team_filter_path(entity, status: 'all') do
All
%fieldset
%legend Projects:
%ul.nav.nav-pills.nav-stacked
- @projects.each do |project|
- unless entities_per_project(project, entity).zero?
%li{class: ("active" if params[:project_id] == project.id.to_s)}
= link_to team_filter_path(entity, project_id: project.id) do
= project.name_with_namespace
%small.right= entities_per_project(project, entity)
%fieldset
%hr
= link_to "Reset", team_filter_path(entity), class: 'btn right'
.projects_box
%h5.title
Projects
%small
(#{projects.count})
- if can? current_user, :manage_user_team, @team
%span.right
= link_to new_team_project_path(@team), class: "btn very_small info" do
%i.icon-plus
Assign Project
%ul.well-list
- if projects.blank?
%p.nothing_here_message This team has no projects yet
- projects.each do |project|
%li
= link_to project_path(project), class: dom_class(project) do
%strong.well-title= truncate(project.name, length: 25)
%span.arrow
&rarr;
%span.last_activity
%strong Last activity:
%span= project_last_activity(project)
%h3.page_title= "Edit Team #{@team.name}"
%hr
= form_for @team, url: 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"
.clearfix
= f.label :path do
Team path is
.input
= f.text_field :path, placeholder: "opensource", class: "xxlarge left"
.clearfix
.input.span3.center
= f.submit 'Save team changes', class: "btn primary"
.input.span3.center
= link_to 'Delete team', team_path(@team), method: :delete, confirm: "You are shure?", class: "btn danger"
%h3.page_title
Issues
%small (in Team projects assigned to Team members)
%small.right #{@issues.total_count} issues
%hr
.row
.span3
= render 'filter', entity: 'issue'
.span9
- if @issues.any?
- @issues.group_by(&:project).each do |group|
%div.ui-box
- @project = group[0]
%h5.title
= link_to_project @project
%ul.well-list.issues_table
- group[1].each do |issue|
= render(partial: 'issues/show', locals: {issue: issue})
%hr
= paginate @issues, theme: "gitlab"
- else
%p.nothing_here_message Nothing to show here
= 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"
- user = member.user
- allow_admin = can? current_user, :manage_user_team, @team
%li{id: dom_id(member), class: "team_member_row user_#{user.id}"}
.row
.span5
= link_to user_path(user.username), title: user.name, class: "dark" do
= image_tag gravatar_icon(user.email, 40), class: "avatar s32"
= link_to user_path(user.username), title: user.name, class: "dark" do
%strong= truncate(user.name, lenght: 40)
%br
%small.cgray= user.email
.span6.right
- if allow_admin
.left.span2
= form_for(member, as: :team_member, url: team_member_path(@team, user)) do |f|
= f.select :permission, options_for_select(UsersProject.access_roles, @team.default_projects_access(user)), {}, class: "medium project-access-select span2"
.left.span2
%span
Admin access
= check_box_tag :group_admin, true, @team.admin?(user)
.right
- if current_user == user
%span.btn.disabled This is you!
- if @team.owner == user
%span.btn.disabled.success Owner
- elsif user.blocked
%span.btn.disabled.blocked Blocked
- elsif allow_admin
= link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "very_small btn danger" do
%i.icon-minus.icon-white
- grouped_user_team_members(@team).each do |access, members|
.ui-box
%h5.title
= Project.access_options.key(access).pluralize
%small= members.size
%ul.well-list
- members.sort_by(&:user_name).each do |up|
= render(partial: 'teams/members/show', locals: {member: up})
:javascript
$(function(){
$('.repo-access-select, .project-access-select').live("change", function() {
$(this.form).submit();
});
})
%h3.page_title
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'
%h3.page_title
Team Members
(#{@members.count})
%small
Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink"
- if can? current_user, :manage_user_team, @team
%span.right
= link_to new_team_member_path(@team), class: "btn success small grouped", title: "New Team Member" do
New Team Member
%hr
.clearfix
%div.team-table
= render partial: "teams/members/team", locals: {project: @team}
%h3.page_title
Team: #{@team.name}
%fieldset
%legend Members (#{@team.members.count})
= form_tag 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
= 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
- allow_admin = can? current_user, :admin_project, @project
- user = @team_member.user
.team_member_show
- if can? current_user, :admin_project, @project
= link_to 'Remove from team', project_team_member_path(project_id: @project, id: @team_member.id), confirm: 'Are you sure?', method: :delete, class: "right btn danger"
.profile_avatar_holder
= image_tag gravatar_icon(user.email, 60), class: "borders"
%h3.page_title
= user.name
%small (@#{user.username})
%hr
.back_link
%br
= link_to project_team_index_path(@project), class: "" do
&larr; To team list
%br
.row
.span6
%table.lite
%tr
%td Email
%td= mail_to user.email
%tr
%td Skype
%td= user.skype
- unless user.linkedin.blank?
%tr
%td LinkedIn
%td= user.linkedin
- unless user.twitter.blank?
%tr
%td Twitter
%td= user.twitter
- unless user.bio.blank?
%tr
%td Bio
%td= user.bio
.span6
%table.lite
%tr
%td Member since
%td= @team_member.created_at.stamp("Aug 21, 2011")
%tr
%td
Project Access:
%small (#{link_to "read more", help_permissions_path, class: "vlink"})
%td
= form_for(@team_member, as: :team_member, url: project_team_member_path(@project, @team_member)) do |f|
= f.select :project_access, options_for_select(Project.access_options, @team_member.project_access), {}, class: "project-access-select", disabled: !allow_admin
%hr
= render @events
:javascript
$(function(){
$('.repo-access-select, .project-access-select').live("change", function() {
$(this.form).submit();
});
})
%h3.page_title
Merge Requests
%small (authored by or assigned to Team members)
%small.right #{@merge_requests.total_count} merge requests
%hr
.row
.span3
= render 'filter', entity: 'merge_request'
.span9
- if @merge_requests.any?
- @merge_requests.group_by(&:project).each do |group|
.ui-box
- @project = group[0]
%h5.title
= link_to_project @project
%ul.well-list
- group[1].each do |merge_request|
= render(partial: 'merge_requests/merge_request', locals: {merge_request: merge_request})
%hr
= paginate @merge_requests, theme: "gitlab"
- else
%h3.nothing_here_message Nothing to show here
%h3.page_title New Team
%hr
= form_for @team, url: 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. Ruby Developers", class: "xxlarge left"
&nbsp;
= 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
= form_tag 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"
%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'
%h3.page_title
Assigned projects (#{@team.projects.count})
%small
Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink"
- if current_user.can?(:manage_user_team, @team) && @avaliable_projects.any?
%span.right
= link_to new_team_project_path(@team), class: "btn success small grouped", title: "New Team Member" do
Assign project to Team
%hr
- if @team.projects.present?
%table.projects-table
%thead
%tr
%th Project name
%th Max access
- if current_user.can?(:admin_user_team, @team)
%th.span3
- @team.projects.each do |project|
%tr.project
%td
= link_to project.name_with_namespace, project_path(project)
%td
%span= @team.human_max_project_access(project)
- if current_user.can?(:admin_user_team, @team)
%td.bgred
= link_to 'Edit max access', edit_team_project_path(@team, project), class: "btn small"
= link_to 'Relegate', team_project_path(@team, project), confirm: 'Remove project from team and move to global namespace. Are you sure?', method: :delete, class: "btn danger small"
- else
%p.nothing_here_message This team has no projects yet
%h3.page_title
Team: #{@team.name}
%fieldset
%legend Projects (#{@team.projects.count})
= form_tag 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, team_project_path(@team, project)
%td
%span= @team.human_max_project_access(project)
%td
%tr
%td= select_tag :project_ids, options_from_collection_for_select(@avaliable_projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
%td= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles), {class: "project-access-select chosen span3" }
%td= submit_tag 'Add', class: "btn primary", id: :assign_projects_to_team
.projects
.activities.span8
= link_to dashboard_path, class: 'btn very_small' do
&larr; To dashboard
&nbsp;
%span.cgray Events and projects are filtered in scope of team
%hr
- if @events.any?
.content_list
- else
%p.nothing_here_message Projects activity will be displayed here
.loading.hide
.side.span4
= render "projects", projects: @projects
%div
%span.rss-icon
= link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
= image_tag "rss_ui.png", title: "feed"
%strong News Feed
%hr
.gitlab-promo
= link_to "Homepage", "http://gitlabhq.com"
= link_to "Blog", "http://blog.gitlabhq.com"
= link_to "@gitlabhq", "https://twitter.com/gitlabhq"
:javascript
$(function(){ Pager.init(20, true); });
.ui-box
%h5.title
Profile
%ul.well-list
%li
%strong Email
%span.right= mail_to @user.email
- unless @user.skype.blank?
%li
%strong Skype
%span.right= @user.skype
- unless @user.linkedin.blank?
%li
%strong LinkedIn
%span.right= @user.linkedin
- unless @user.twitter.blank?
%li
%strong Twitter
%span.right= @user.twitter
- unless @user.bio.blank?
%li
%strong Bio
%span.right= @user.bio
......@@ -3,6 +3,11 @@
%h3.page_title
= image_tag gravatar_icon(@user.email, 90), class: "avatar s90"
= @user.name
- if @user == current_user
.right
= link_to profile_path, class: 'btn small' do
%i.icon-edit
Edit Profile
%br
%small @#{@user.username}
%br
......@@ -12,26 +17,5 @@
%h5 Recent events
= render @events
.span4
.ui-box
%h5.title Profile
%ul.well-list
%li
%strong Email
%span.right= mail_to @user.email
- unless @user.skype.blank?
%li
%strong Skype
%span.right= @user.skype
- unless @user.linkedin.blank?
%li
%strong LinkedIn
%span.right= @user.linkedin
- unless @user.twitter.blank?
%li
%strong Twitter
%span.right= @user.twitter
- unless @user.bio.blank?
%li
%strong Bio
%span.right= @user.bio
= render 'profile'
= render 'projects'
class ProjectWebHookWorker
include Sidekiq::Worker
sidekiq_options queue: :project_web_hook
def perform(hook_id, data)
WebHook.find(hook_id).execute data
end
end
......@@ -21,7 +21,7 @@ Gitlab::Application.routes.draw do
project_root: Gitlab.config.gitolite.repos_path,
upload_pack: Gitlab.config.gitolite.upload_pack,
receive_pack: Gitlab.config.gitolite.receive_pack
}), at: '/', constraints: lambda { |request| /[-\/\w\.-]+\.git\//.match(request.path_info) }
}), at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }
#
# Help
......@@ -49,13 +49,14 @@ Gitlab::Application.routes.draw do
# Admin Area
#
namespace :admin do
resources :users do
resources :users, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do
member do
put :team_update
put :block
put :unblock
end
end
resources :groups, constraints: { id: /[^\/]+/ } do
member do
put :project_update
......@@ -63,18 +64,31 @@ Gitlab::Application.routes.draw do
delete :remove_project
end
end
resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, except: [:new, :create] do
member do
get :team
put :team_update
resources :teams, constraints: { id: /[^\/]+/ } do
scope module: :teams do
resources :members, only: [:edit, :update, :destroy, :new, :create]
resources :projects, only: [:edit, :update, :destroy, :new, :create], constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }
end
end
resources :team_members, only: [:edit, :update, :destroy]
resources :hooks, only: [:index, :create, :destroy] do
get :test
end
resource :logs, only: [:show]
resource :resque, controller: 'resque', only: [:show]
resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, except: [:new, :create] do
member do
get :team
put :team_update
end
scope module: :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ } do
resources :members, only: [:edit, :update, :destroy]
end
end
root to: "dashboard#index"
end
......@@ -104,15 +118,18 @@ Gitlab::Application.routes.draw do
#
# Dashboard Area
#
get "dashboard" => "dashboard#index"
get "dashboard/issues" => "dashboard#issues"
get "dashboard/merge_requests" => "dashboard#merge_requests"
resource :dashboard, controller: "dashboard" do
member do
get :projects
get :issues
get :merge_requests
end
end
#
# Groups Area
#
resources :groups, constraints: { id: /[^\/]+/ }, only: [:show] do
resources :groups, constraints: { id: /[^\/]+/ }, only: [:show, :new, :create] do
member do
get :issues
get :merge_requests
......@@ -122,6 +139,20 @@ Gitlab::Application.routes.draw do
end
end
#
# Teams Area
#
resources :teams, constraints: { id: /[^\/]+/ } do
member do
get :issues
get :merge_requests
end
scope module: :teams do
resources :members, only: [:index, :new, :create, :edit, :update, :destroy]
resources :projects, only: [:index, :new, :create, :edit, :update, :destroy], constraints: { id: /[a-zA-Z.0-9_\-\/]+/ }
end
end
resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create]
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations }
......@@ -238,6 +269,18 @@ Gitlab::Application.routes.draw do
end
end
scope module: :projects do
resources :teams, only: [] do
collection do
get :available
post :assign
end
member do
delete :resign
end
end
end
resources :notes, only: [:index, :create, :destroy] do
collection do
post :preview
......@@ -245,5 +288,5 @@ Gitlab::Application.routes.draw do
end
end
root to: "dashboard#index"
root to: "dashboard#show"
end
class CreateUserTeams < ActiveRecord::Migration
def change
create_table :user_teams do |t|
t.string :name
t.string :path
t.integer :owner_id
t.timestamps
end
end
end
class CreateUserTeamProjectRelationships < ActiveRecord::Migration
def change
create_table :user_team_project_relationships do |t|
t.integer :project_id
t.integer :user_team_id
t.integer :greatest_access
t.timestamps
end
end
end
class CreateUserTeamUserRelationships < ActiveRecord::Migration
def change
create_table :user_team_user_relationships do |t|
t.integer :user_id
t.integer :user_team_id
t.boolean :group_admin
t.integer :permission
t.timestamps
end
end
end
class AddUserPermissions < ActiveRecord::Migration
def up
add_column :users, :can_create_group, :boolean, default: true, null: false
add_column :users, :can_create_team, :boolean, default: true, null: false
end
def down
remove_column :users, :can_create_group
remove_column :users, :can_create_team
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20130110172407) do
ActiveRecord::Schema.define(:version => 20130125090214) do
create_table "events", :force => true do |t|
t.string "target_type"
......@@ -213,6 +213,31 @@ ActiveRecord::Schema.define(:version => 20130110172407) do
t.string "name"
end
create_table "user_team_project_relationships", :force => true do |t|
t.integer "project_id"
t.integer "user_team_id"
t.integer "greatest_access"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "user_team_user_relationships", :force => true do |t|
t.integer "user_id"
t.integer "user_team_id"
t.boolean "group_admin"
t.integer "permission"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "user_teams", :force => true do |t|
t.string "name"
t.string "path"
t.integer "owner_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "users", :force => true do |t|
t.string "email", :default => "", :null => false
t.string "encrypted_password", :default => "", :null => false
......@@ -242,6 +267,8 @@ ActiveRecord::Schema.define(:version => 20130110172407) do
t.string "extern_uid"
t.string "provider"
t.string "username"
t.boolean "can_create_group", :default => true, :null => false
t.boolean "can_create_team", :default => true, :null => false
end
add_index "users", ["admin"], :name => "index_users_on_admin"
......
Feature: Admin Teams
Background:
Given I sign in as an admin
And Create gitlab user "John"
Scenario: Create a team
When I visit admin teams page
And I click new team link
And submit form with new team info
Then I should be redirected to team page
And I should see newly created team
Scenario: Add user to team
When I visit admin teams page
When I have clean "HardCoders" team
And I visit "HardCoders" team page
When I click to "Add members" link
When I select user "John" from user list as "Developer"
And submit form with new team member info
Then I should see "John" in teams members list as "Developer"
Scenario: Assign team to existing project
When I visit admin teams page
When I have "HardCoders" team with "John" member with "Developer" role
When I have "Shop" project
And I visit "HardCoders" team page
Then I should see empty projects table
When I click to "Add projects" link
When I select project "Shop" with max access "Reporter"
And submit form with new team project info
Then I should see "Shop" project in projects list
When I visit "Shop" project admin page
Then I should see "John" user with role "Reporter" in team table
Scenario: Add user to team with ptojects
When I visit admin teams page
When I have "HardCoders" team with "John" member with "Developer" role
And "HardCoders" team assigned to "Shop" project with "Developer" max role access
When I have gitlab user "Jimm"
And I visit "HardCoders" team page
Then I should see members table without "Jimm" member
When I click to "Add members" link
When I select user "Jimm" ub team members list as "Master"
And submit form with new team member info
Then I should see "Jimm" in teams members list as "Master"
Scenario: Remove member from team
Given I have users team "HardCoders"
And gitlab user "John" is a member "HardCoders" team
And gitlab user "Jimm" is a member "HardCoders" team
And "HardCoders" team is assigned to "Shop" project
When I visit admin teams page
When I visit "HardCoders" team admin page
Then I shoould see "John" in members list
And I should see "Jimm" in members list
And I should see "Shop" in projects list
When I click on remove "Jimm" user link
Then I should be redirected to "HardCoders" team admin page
And I should not to see "Jimm" user in members list
Scenario: Remove project from team
Given I have users team "HardCoders"
And gitlab user "John" is a member "HardCoders" team
And gitlab user "Jimm" is a member "HardCoders" team
And "HardCoders" team is assigned to "Shop" project
When I visit admin teams page
When I visit "HardCoders" team admin page
Then I should see "Shop" project in projects list
When I click on "Relegate" link on "Shop" project
Then I should see projects liston team page without "Shop" project
......@@ -16,12 +16,6 @@ Feature: Dashboard
And I visit dashboard page
Then I should see groups list
Scenario: I should see correct projects count
Given I have group with projects
And group has a projects that does not belongs to me
When I visit dashboard page
Then I should see 1 project at group list
Scenario: I should see last push widget
Then I should see last push widget
And I click "Create Merge Request" link
......
Feature: Dashboard
Background:
Given I sign in as a user
And I own project "Shop"
And I visit dashboard projects page
Scenario: I should see issues list
Then I should see projects list
Feature: Groups
Background:
Given I sign in as a user
Scenario: Create a group from dasboard
Given I have group with projects
And I visit dashboard page
When I click new group link
And submit form with new group info
Then I should be redirected to group page
And I should see newly created group
class AdminTeams < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedActiveTab
include SharedAdmin
And 'I have own project' do
create :project
end
And 'Create gitlab user "John"' do
@user = create(:user, :name => "John")
end
And 'I click new team link' do
click_link "New Team"
end
And 'submit form with new team info' do
fill_in 'user_team_name', with: 'gitlab'
click_button 'Create team'
end
Then 'I should be redirected to team page' do
current_path.should == admin_team_path(UserTeam.last)
end
And 'I should see newly created team' do
page.should have_content "Team: gitlab"
end
When 'I visit admin teams page' do
visit admin_teams_path
end
When 'I have clean "HardCoders" team' do
@team = create :user_team, name: "HardCoders", owner: current_user
end
And 'I visit "HardCoders" team page' do
visit admin_team_path(UserTeam.find_by_name("HardCoders"))
end
Then 'I should see only me in members table' do
members_list = find("#members_list .member")
members_list.should have_content(current_user.name)
members_list.should have_content(current_user.email)
end
When 'I select user "John" from user list as "Developer"' do
@user ||= User.find_by_name("John")
within "#team_members" do
select @user.name, :from => "user_ids"
select "Developer", :from => "default_project_access"
end
end
And 'submit form with new team member info' do
click_button 'add_members_to_team'
end
Then 'I should see "John" in teams members list as "Developer"' do
@user ||= User.find_by_name("John")
find_in_list("#members_list .member", @user).must_equal true
end
When 'I visit "John" user admin page' do
pending 'step not implemented'
end
Then 'I should see "HardCoders" team in teams table' do
pending 'step not implemented'
end
When 'I have "HardCoders" team with "John" member with "Developer" role' do
@team = create :user_team, name: "HardCoders", owner: current_user
@user ||= User.find_by_name("John")
@team.add_member(@user, UserTeam.access_roles["Developer"], group_admin: false)
end
When 'I have "Shop" project' do
@project = create :project, name: "Shop"
end
Then 'I should see empty projects table' do
page.has_no_css?("#projects_list").must_equal true
end
When 'I select project "Shop" with max access "Reporter"' do
@project ||= Project.find_by_name("Shop")
within "#assign_projects" do
select @project.name, :from => "project_ids"
select "Reporter", :from => "greatest_project_access"
end
end
And 'submit form with new team project info' do
click_button 'assign_projects_to_team'
end
Then 'I should see "Shop" project in projects list' do
project = Project.find_by_name("Shop")
find_in_list("#projects_list .project", project).must_equal true
end
When 'I visit "Shop" project admin page' do
project = Project.find_by_name("Shop")
visit admin_project_path(project)
end
And '"HardCoders" team assigned to "Shop" project with "Developer" max role access' do
@team = UserTeam.find_by_name("HardCoders")
@project = create :project, name: "Shop"
@team.assign_to_project(@project, UserTeam.access_roles["Developer"])
end
When 'I have gitlab user "Jimm"' do
create :user, name: "Jimm"
end
Then 'I should see members table without "Jimm" member' do
user = User.find_by_name("Jimm")
find_in_list("#members_list .member", user).must_equal false
end
When 'I select user "Jimm" ub team members list as "Master"' do
user = User.find_by_name("Jimm")
within "#team_members" do
select user.name, :from => "user_ids"
select "Developer", :from => "default_project_access"
end
end
Then 'I should see "Jimm" in teams members list as "Master"' do
user = User.find_by_name("Jimm")
find_in_list("#members_list .member", user).must_equal true
end
Given 'I have users team "HardCoders"' do
@team = create :user_team, name: "HardCoders"
end
And 'gitlab user "John" is a member "HardCoders" team' do
@team = UserTeam.find_by_name("HardCoders")
@user = User.find_by_name("John")
@user = create :user, name: "John" unless @user
@team.add_member(@user, UserTeam.access_roles["Master"], group_admin: false)
end
And 'gitlab user "Jimm" is a member "HardCoders" team' do
@team = UserTeam.find_by_name("HardCoders")
@user = User.find_by_name("Jimm")
@user = create :user, name: "Jimm" unless @user
@team.add_member(@user, UserTeam.access_roles["Master"], group_admin: false)
end
And '"HardCoders" team is assigned to "Shop" project' do
@team = UserTeam.find_by_name("HardCoders")
@project = create :project, name: "Shop"
@team.assign_to_project(@project, UserTeam.access_roles["Developer"])
end
When 'I visit "HardCoders" team admin page' do
visit admin_team_path(UserTeam.find_by_name("HardCoders"))
end
Then 'I shoould see "John" in members list' do
user = User.find_by_name("John")
find_in_list("#members_list .member", user).must_equal true
end
And 'I should see "Jimm" in members list' do
user = User.find_by_name("Jimm")
find_in_list("#members_list .member", user).must_equal true
end
And 'I should see "Shop" in projects list' do
project = Project.find_by_name("Shop")
find_in_list("#projects_list .project", project).must_equal true
end
When 'I click on remove "Jimm" user link' do
user = User.find_by_name("Jimm")
click_link "remove_member_#{user.id}"
end
Then 'I should be redirected to "HardCoders" team admin page' do
current_path.should == admin_team_path(UserTeam.find_by_name("HardCoders"))
end
And 'I should not to see "Jimm" user in members list' do
user = User.find_by_name("Jimm")
find_in_list("#members_list .member", user).must_equal false
end
When 'I click on "Relegate" link on "Shop" project' do
project = Project.find_by_name("Shop")
click_link "relegate_project_#{project.id}"
end
Then 'I should see projects liston team page without "Shop" project' do
project = Project.find_by_name("Shop")
find_in_list("#projects_list .project", project).must_equal false
end
Then 'I should see "John" user with role "Reporter" in team table' do
user = User.find_by_name("John")
find_in_list(".team_members", user).must_equal true
end
When 'I click to "Add members" link' do
click_link "Add members"
end
When 'I click to "Add projects" link' do
click_link "Add projects"
end
protected
def current_team
@team ||= Team.first
end
def find_in_list(selector, item)
members_list = all(selector)
entered = false
members_list.each do |member_item|
entered = true if member_item.has_content?(item.name)
end
entered
end
end
......@@ -63,6 +63,12 @@ class Dashboard < Spinach::FeatureSteps
@project.team << [current_user, :master]
end
Then 'I should see projects list' do
@user.authorized_projects.all.each do |project|
page.should have_link project.name_with_namespace
end
end
Then 'I should see groups list' do
Group.all.each do |group|
page.should have_link group.name
......
......@@ -64,6 +64,24 @@ class Groups < Spinach::FeatureSteps
author: current_user
end
When 'I click new group link' do
click_link "New Group"
end
And 'submit form with new group info' do
fill_in 'group_name', :with => 'Samurai'
click_button "Create group"
end
Then 'I should see newly created group' do
page.should have_content "Samurai"
page.should have_content "You will only see events from projects in this group"
end
Then 'I should be redirected to group page' do
current_path.should == group_path(Group.last)
end
protected
def current_group
......
......@@ -2,27 +2,27 @@ module SharedDiffNote
include Spinach::DSL
Given 'I cancel the diff comment' do
within(".diff_file") do
within(".file") do
find(".js-close-discussion-note-form").trigger("click")
end
end
Given 'I delete a diff comment' do
sleep 1
within(".diff_file") do
within(".file") do
first(".js-note-delete").trigger("click")
end
end
Given 'I haven\'t written any diff comment text' do
within(".diff_file") do
within(".file") do
fill_in "note[note]", with: ""
end
end
Given 'I leave a diff comment like "Typo, please fix"' do
find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click")
within(".diff_file") do
within(".file") do
fill_in "note[note]", with: "Typo, please fix"
#click_button("Add Comment")
find(".js-comment-button").trigger("click")
......@@ -32,7 +32,7 @@ module SharedDiffNote
Given 'I preview a diff comment text like "Should fix it :smile:"' do
find("#586fb7c4e1add2d4d24e27566ed7064680098646_29_14.line_holder .js-add-diff-note-button").trigger("click")
within(".diff_file") do
within(".file") do
fill_in "note[note]", with: "Should fix it :smile:"
find(".js-note-preview-button").trigger("click")
end
......@@ -40,7 +40,7 @@ module SharedDiffNote
Given 'I preview another diff comment text like "DRY this up"' do
find("#586fb7c4e1add2d4d24e27566ed7064680098646_57_41.line_holder .js-add-diff-note-button").trigger("click")
within(".diff_file") do
within(".file") do
fill_in "note[note]", with: "DRY this up"
find(".js-note-preview-button").trigger("click")
end
......@@ -55,13 +55,13 @@ module SharedDiffNote
end
Given 'I write a diff comment like ":-1: I don\'t like this"' do
within(".diff_file") do
within(".file") do
fill_in "note[note]", with: ":-1: I don\'t like this"
end
end
Given 'I submit the diff comment' do
within(".diff_file") do
within(".file") do
click_button("Add Comment")
end
end
......@@ -69,49 +69,49 @@ module SharedDiffNote
Then 'I should not see the diff comment form' do
within(".diff_file") do
within(".file") do
page.should_not have_css("form.new_note")
end
end
Then 'I should not see the diff comment preview button' do
within(".diff_file") do
within(".file") do
page.should have_css(".js-note-preview-button", visible: false)
end
end
Then 'I should not see the diff comment text field' do
within(".diff_file") do
within(".file") do
page.should have_css(".js-note-text", visible: false)
end
end
Then 'I should only see one diff form' do
within(".diff_file") do
within(".file") do
page.should have_css("form.new_note", count: 1)
end
end
Then 'I should see a diff comment form with ":-1: I don\'t like this"' do
within(".diff_file") do
within(".file") do
page.should have_field("note[note]", with: ":-1: I don\'t like this")
end
end
Then 'I should see a diff comment saying "Typo, please fix"' do
within(".diff_file .note") do
within(".file .note") do
page.should have_content("Typo, please fix")
end
end
Then 'I should see a discussion reply button' do
within(".diff_file") do
within(".file") do
page.should have_link("Reply")
end
end
Then 'I should see a temporary diff comment form' do
within(".diff_file") do
within(".file") do
page.should have_css(".js-temp-notes-holder form.new_note")
end
end
......@@ -121,37 +121,37 @@ module SharedDiffNote
end
Then 'I should see an empty diff comment form' do
within(".diff_file") do
within(".file") do
page.should have_field("note[note]", with: "")
end
end
Then 'I should see the cancel comment button' do
within(".diff_file form") do
within(".file form") do
page.should have_css(".js-close-discussion-note-form", text: "Cancel")
end
end
Then 'I should see the diff comment preview' do
within(".diff_file form") do
within(".file form") do
page.should have_css(".js-note-preview", visible: false)
end
end
Then 'I should see the diff comment edit button' do
within(".diff_file") do
within(".file") do
page.should have_css(".js-note-edit-button", visible: true)
end
end
Then 'I should see the diff comment preview button' do
within(".diff_file") do
within(".file") do
page.should have_css(".js-note-preview-button", visible: true)
end
end
Then 'I should see two separate previews' do
within(".diff_file") do
within(".file") do
page.should have_css(".js-note-preview", visible: true, count: 2)
page.should have_content("Should fix it")
page.should have_content("DRY this up")
......
......@@ -33,12 +33,16 @@ module SharedPaths
visit dashboard_path
end
Given 'I visit dashboard projects page' do
visit projects_dashboard_path
end
Given 'I visit dashboard issues page' do
visit dashboard_issues_path
visit issues_dashboard_path
end
Given 'I visit dashboard merge requests page' do
visit dashboard_merge_requests_path
visit merge_requests_dashboard_path
end
Given 'I visit dashboard search page' do
......@@ -105,6 +109,10 @@ module SharedPaths
visit admin_groups_path
end
When 'I visit admin teams page' do
visit admin_teams_path
end
# ----------------------------------------
# Generic Project
# ----------------------------------------
......
class Userteams < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedProject
When 'I do not have teams with me' do
UserTeam.with_member(current_user).destroy_all
end
Then 'I should see dashboard page without teams info block' do
page.has_no_css?(".teams-box").must_equal true
end
When 'I have teams with my membership' do
team = create :user_team, owner: current_user
team.add_member(current_user, UserTeam.access_roles["Master"], true)
end
Then 'I should see dashboard page with teams information block' do
page.should have_css(".teams-box")
end
When 'exist user teams' do
team = create :user_team
team.add_member(current_user, UserTeam.access_roles["Master"], true)
end
And 'I click on "All teams" link' do
click_link("All Teams")
end
Then 'I should see "All teams" page' do
current_path.should == teams_path
end
And 'I should see exist teams in teams list' do
team = UserTeam.last
find_in_list(".teams_list tr", team).must_equal true
end
When 'I click to "New team" link' do
click_link("New Team")
end
And 'I submit form with new team info' do
fill_in 'name', with: 'gitlab'
click_button 'Create team'
end
Then 'I should be redirected to new team page' do
team = UserTeam.last
current_path.should == team_path(team)
end
When 'I have teams with projects and members' do
team = create :user_team, owner: current_user
@project = create :project
team.add_member(current_user, UserTeam.access_roles["Master"], true)
team.assign_to_project(@project, UserTeam.access_roles["Master"])
@event = create(:closed_issue_event, project: @project)
end
When 'I visit team page' do
visit team_path(UserTeam.last)
end
Then 'I should see projects list' do
page.should have_css(".projects_box")
projects_box = find(".projects_box")
projects_box.should have_content(@project.name)
end
And 'project from team has issues assigned to me' do
team = UserTeam.last
team.projects.each do |project|
project.issues << create(:issue, assignee: current_user)
end
end
When 'I visit team issues page' do
team = UserTeam.last
visit issues_team_path(team)
end
Then 'I should see issues from this team assigned to me' do
team = UserTeam.last
team.projects.each do |project|
project.issues.assigned(current_user).each do |issue|
page.should have_content issue.title
end
end
end
Given 'I have team with projects and members' do
team = create :user_team, owner: current_user
project = create :project
user = create :user
team.add_member(current_user, UserTeam.access_roles["Master"], true)
team.add_member(user, UserTeam.access_roles["Developer"], false)
team.assign_to_project(project, UserTeam.access_roles["Master"])
end
Given 'project from team has issues assigned to teams members' do
team = UserTeam.last
team.projects.each do |project|
team.members.each do |member|
project.issues << create(:issue, assignee: member)
end
end
end
Then 'I should see issues from this team assigned to teams members' do
team = UserTeam.last
team.projects.each do |project|
team.members.each do |member|
project.issues.assigned(member).each do |issue|
page.should have_content issue.title
end
end
end
end
Given 'project from team has merge requests assigned to me' do
team = UserTeam.last
team.projects.each do |project|
team.members.each do |member|
3.times { project.merge_requests << create(:merge_request, assignee: member) }
end
end
end
When 'I visit team merge requests page' do
team = UserTeam.last
visit merge_requests_team_path(team)
end
Then 'I should see merge requests from this team assigned to me' do
team = UserTeam.last
team.projects.each do |project|
team.members.each do |member|
project.issues.assigned(member).each do |merge_request|
page.should have_content merge_request.title
end
end
end
end
Given 'project from team has merge requests assigned to team members' do
team = UserTeam.last
team.projects.each do |project|
team.members.each do |member|
3.times { project.merge_requests << create(:merge_request, assignee: member) }
end
end
end
Then 'I should see merge requests from this team assigned to me' do
team = UserTeam.last
team.projects.each do |project|
team.members.each do |member|
project.issues.assigned(member).each do |merge_request|
page.should have_content merge_request.title
end
end
end
end
Given 'I have new user "John"' do
create :user, name: "John"
end
When 'I visit team people page' do
team = UserTeam.last
visit team_members_path(team)
end
And 'I select user "John" from list with role "Reporter"' do
user = User.find_by_name("John")
within "#team_members" do
select user.name, :from => "user_ids"
select "Reporter", :from => "default_project_access"
end
click_button "Add"
end
Then 'I should see user "John" in team list' do
user = User.find_by_name("John")
team_members_list = find(".team-table")
team_members_list.should have_content user.name
end
And 'I have my own project without teams' do
@project = create :project, namespace: current_user.namespace
end
And 'I visit my team page' do
team = UserTeam.where(owner_id: current_user.id).last
visit team_path(team)
end
When 'I click on link "Projects"' do
click_link "Projects"
end
And 'I click link "Assign project to Team"' do
click_link "Assign project to Team"
end
Then 'I should see form with my own project in avaliable projects list' do
projects_select = find("#project_ids")
projects_select.should have_content(@project.name)
end
When 'I submit form with selected project and max access' do
within "#assign_projects" do
select @project.name_with_namespace, :from => "project_ids"
select "Reporter", :from => "greatest_project_access"
end
click_button "Add"
end
Then 'I should see my own project in team projects list' do
projects = find(".projects-table")
projects.should have_content(@project.name)
end
When 'I click link "New Team Member"' do
click_link "New Team Member"
end
protected
def current_team
@user_team ||= UserTeam.first
end
def project
current_team.projects.first
end
def assigned_to_user key, user
project.send(key).where(assignee_id: user)
end
def find_in_list(selector, item)
members_list = all(selector)
entered = false
members_list.each do |member_item|
entered = true if member_item.has_content?(item.name)
end
entered
end
end
......@@ -21,7 +21,6 @@ Dir["#{Rails.root}/features/steps/shared/*.rb"].each {|file| require file}
include GitoliteStub
WebMock.allow_net_connect!
#
# JS driver
#
......
Feature: UserTeams
Background:
Given I sign in as a user
And I own project "Shop"
And project "Shop" has push event
Scenario: No teams, no dashboard info block
When I do not have teams with me
And I visit dashboard page
Then I should see dashboard page without teams info block
Scenario: I should see teams info block
When I have teams with my membership
And I visit dashboard page
Then I should see dashboard page with teams information block
Scenario: I should can create new team
When I have teams with my membership
And I visit dashboard page
When I click to "New team" link
And I submit form with new team info
Then I should be redirected to new team page
Scenario: I should see team dashboard list
When I have teams with projects and members
When I visit team page
Then I should see projects list
Scenario: I should see team issues list
Given I have team with projects and members
And project from team has issues assigned to me
When I visit team issues page
Then I should see issues from this team assigned to me
Scenario: I should see teams members issues list
Given I have team with projects and members
Given project from team has issues assigned to teams members
When I visit team issues page
Then I should see issues from this team assigned to teams members
Scenario: I should see team merge requests list
Given I have team with projects and members
Given project from team has merge requests assigned to me
When I visit team merge requests page
Then I should see merge requests from this team assigned to me
Scenario: I should see teams members merge requests list
Given I have team with projects and members
Given project from team has merge requests assigned to team members
When I visit team merge requests page
Then I should see merge requests from this team assigned to me
Scenario: I should add user to projects in Team
Given I have team with projects and members
Given I have new user "John"
When I visit team people page
When I click link "New Team Member"
And I select user "John" from list with role "Reporter"
Then I should see user "John" in team list
Scenario: I should assign my team to my own project
Given I have team with projects and members
And I have my own project without teams
And I visit my team page
When I click on link "Projects"
And I click link "Assign project to Team"
Then I should see form with my own project in avaliable projects list
When I submit form with selected project and max access
Then I should see my own project in team projects list
......@@ -88,7 +88,10 @@ module Gitlab
end
def destroy_project(project)
FileUtils.rm_rf(project.repository.path_to_repo)
# do rm-rf only if repository exists
if project.repository
FileUtils.rm_rf(project.repository.path_to_repo)
end
conf.rm_repo(project.path_with_namespace)
end
......
# UserTeamManager class
#
# Used for manage User teams with project repositories
module Gitlab
class UserTeamManager
class << self
def assign(team, project, access)
project = Project.find(project) unless project.is_a? Project
searched_project = team.user_team_project_relationships.find_by_project_id(project.id)
unless searched_project.present?
team.user_team_project_relationships.create(project_id: project.id, greatest_access: access)
update_team_users_access_in_project(team, project)
end
end
def resign(team, project)
project = Project.find(project) unless project.is_a? Project
team.user_team_project_relationships.with_project(project).destroy_all
update_team_users_access_in_project(team, project)
end
def update_team_user_membership(team, member, options)
updates = {}
if options[:default_projects_access] && options[:default_projects_access] != team.default_projects_access(member)
updates[:permission] = options[:default_projects_access]
end
if options[:group_admin].to_s != team.admin?(member).to_s
updates[:group_admin] = options[:group_admin].present?
end
unless updates.blank?
user_team_relationship = team.user_team_user_relationships.find_by_user_id(member)
if user_team_relationship.update_attributes(updates)
if updates[:permission]
rebuild_project_permissions_to_member(team, member)
end
true
else
false
end
else
true
end
end
def update_project_greates_access(team, project, permission)
project_relation = team.user_team_project_relationships.find_by_project_id(project)
if permission != team.max_project_access(project)
if project_relation.update_attributes(greatest_access: permission)
update_team_users_access_in_project(team, project)
true
else
false
end
else
true
end
end
def rebuild_project_permissions_to_member(team, member)
team.projects.each do |project|
update_team_user_access_in_project(team, member, project)
end
end
def update_team_users_access_in_project(team, project)
members = team.members
members.each do |member|
update_team_user_access_in_project(team, member, project)
end
end
def update_team_user_access_in_project(team, user, project)
granted_access = max_teams_member_permission_in_project(user, project)
project_team_user = UsersProject.find_by_user_id_and_project_id(user.id, project.id)
project_team_user.destroy if project_team_user.present?
# project_team_user.project_access != granted_access
project.team << [user, granted_access] if granted_access > 0
end
def max_teams_member_permission_in_project(user, project, teams = nil)
result_access = 0
user_teams = project.user_teams.with_member(user)
teams ||= user_teams
if teams.any?
teams.each do |team|
granted_access = max_team_member_permission_in_project(team, user, project)
result_access = [granted_access, result_access].max
end
end
result_access
end
def max_team_member_permission_in_project(team, user, project)
member_access = team.default_projects_access(user)
team_access = team.user_team_project_relationships.find_by_project_id(project.id).greatest_access
[team_access, member_access].min
end
def add_member_into_team(team, user, access, admin)
user = User.find(user) unless user.is_a? User
team.user_team_user_relationships.create(user_id: user.id, permission: access, group_admin: admin)
team.projects.each do |project|
update_team_user_access_in_project(team, user, project)
end
end
def remove_member_from_team(team, user)
user = User.find(user) unless user.is_a? User
team.user_team_user_relationships.with_user(user).destroy_all
other_teams = []
team.projects.each do |project|
other_teams << project.user_teams.with_member(user)
end
other_teams.uniq
unless other_teams.any?
UsersProject.in_projects(team.projects).with_user(user).destroy_all
end
end
end
end
end
......@@ -169,7 +169,7 @@ namespace :gitlab do
else
puts "no".red
try_fixing_it(
sudo_gitlab("bundle exec rake db:migrate")
sudo_gitlab("bundle exec rake db:migrate RAILS_ENV=production")
)
fix_and_rerun
end
......@@ -194,7 +194,7 @@ namespace :gitlab do
else
puts "no".red
try_fixing_it(
sudo_gitlab("bundle exec rake gitlab:satellites:create"),
sudo_gitlab("bundle exec rake gitlab:satellites:create RAILS_ENV=production"),
"If necessary, remove the tmp/repo_satellites directory ...",
"... and rerun the above command"
)
......@@ -789,7 +789,7 @@ namespace :gitlab do
else
puts "wrong or missing".red
try_fixing_it(
sudo_gitlab("bundle exec rake gitlab:gitolite:update_repos")
sudo_gitlab("bundle exec rake gitlab:gitolite:update_repos RAILS_ENV=production")
)
for_more_information(
"doc/raketasks/maintenance.md"
......@@ -895,7 +895,7 @@ namespace :gitlab do
else
puts "no".red
try_fixing_it(
sudo_gitlab("bundle exec rake sidekiq:start")
sudo_gitlab("bundle exec rake sidekiq:start RAILS_ENV=production")
)
for_more_information(
see_installation_guide_section("Install Init Script"),
......
......@@ -6,7 +6,7 @@ namespace :sidekiq do
desc "GITLAB | Start sidekiq"
task :start do
run "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
run "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
end
def pidfile
......
<!DOCTYPE html>
<html>
<head>
<title>Deploy in progress. Please try again in few minutes</title>
<link href="/static.css" media="screen" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>Deploy in progress</h1>
<h3>Please try again in few minutes or contact your administrator.</h3>
</body>
</html>
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :user_team_project_relationship do
project
user_team
greatest_access { UsersProject::MASTER }
end
end
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :user_team_user_relationship do
user
user_team
group_admin false
permission { UsersProject::MASTER }
end
end
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :user_team do
sequence(:name) { |n| "team#{n}" }
path { name.downcase.gsub(/\s/, '_') }
owner
end
end
......@@ -38,11 +38,14 @@ describe Project, "Hooks" do
@project_hook = create(:project_hook)
@project_hook_2 = create(:project_hook)
project.hooks << [@project_hook, @project_hook_2]
stub_request(:post, @project_hook.url)
stub_request(:post, @project_hook_2.url)
end
it "executes multiple web hook" do
@project_hook.should_receive(:execute).once
@project_hook_2.should_receive(:execute).once
@project_hook.should_receive(:async_execute).once
@project_hook_2.should_receive(:async_execute).once
project.trigger_post_receive('oldrev', 'newrev', 'refs/heads/master', @user)
end
......
require "spec_helper"
describe Team do
describe ProjectTeam do
let(:team) { create(:project).team }
describe "Respond to" do
......
......@@ -189,7 +189,7 @@ describe User do
it { user.is_admin?.should be_false }
it { user.require_ssh_key?.should be_true }
it { user.can_create_group?.should be_false }
it { user.can_create_group?.should be_true }
it { user.can_create_project?.should be_true }
it { user.first_name.should == 'John' }
end
......
require 'spec_helper'
describe UserTeamProjectRelationship do
pending "add some examples to (or delete) #{__FILE__}"
end
require 'spec_helper'
describe UserTeam do
pending "add some examples to (or delete) #{__FILE__}"
end
require 'spec_helper'
describe UserTeamUserRelationship do
pending "add some examples to (or delete) #{__FILE__}"
end
......@@ -10,7 +10,7 @@ describe "Dashboard Issues Feed" do
describe "atom feed" do
it "should render atom feed via private token" do
visit dashboard_issues_path(:atom, private_token: user.private_token)
visit issues_dashboard_path(:atom, private_token: user.private_token)
page.response_headers['Content-Type'].should have_content("application/atom+xml")
page.body.should have_selector("title", text: "#{user.name} issues")
......
......@@ -95,20 +95,20 @@ describe Admin::ProjectsController, "routing" do
end
end
# edit_admin_team_member GET /admin/team_members/:id/edit(.:format) admin/team_members#edit
# admin_team_member PUT /admin/team_members/:id(.:format) admin/team_members#update
# DELETE /admin/team_members/:id(.:format) admin/team_members#destroy
describe Admin::TeamMembersController, "routing" do
# edit_admin_project_member GET /admin/projects/:project_id/members/:id/edit(.:format) admin/projects/members#edit {:id=>/[^\/]+/, :project_id=>/[^\/]+/}
# admin_project_member PUT /admin/projects/:project_id/members/:id(.:format) admin/projects/members#update {:id=>/[^\/]+/, :project_id=>/[^\/]+/}
# DELETE /admin/projects/:project_id/members/:id(.:format) admin/projects/members#destroy {:id=>/[^\/]+/, :project_id=>/[^\/]+/}
describe Admin::Projects::MembersController, "routing" do
it "to #edit" do
get("/admin/team_members/1/edit").should route_to('admin/team_members#edit', id: '1')
get("/admin/projects/test/members/1/edit").should route_to('admin/projects/members#edit', project_id: 'test', id: '1')
end
it "to #update" do
put("/admin/team_members/1").should route_to('admin/team_members#update', id: '1')
put("/admin/projects/test/members/1").should route_to('admin/projects/members#update', project_id: 'test', id: '1')
end
it "to #destroy" do
delete("/admin/team_members/1").should route_to('admin/team_members#destroy', id: '1')
delete("/admin/projects/test/members/1").should route_to('admin/projects/members#destroy', project_id: 'test', id: '1')
end
end
......
......@@ -146,14 +146,14 @@ describe KeysController, "routing" do
end
end
# dashboard GET /dashboard(.:format) dashboard#index
# dashboard GET /dashboard(.:format) dashboard#show
# dashboard_issues GET /dashboard/issues(.:format) dashboard#issues
# dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests
# root / dashboard#index
# root / dashboard#show
describe DashboardController, "routing" do
it "to #index" do
get("/dashboard").should route_to('dashboard#index')
get("/").should route_to('dashboard#index')
get("/dashboard").should route_to('dashboard#show')
get("/").should route_to('dashboard#show')
end
it "to #issues" do
......
......@@ -65,15 +65,15 @@
BranchGraph.prototype.buildGraph = function(){
var graphWidth = $(this.element).width()
, ch = this.mspace * 20 + 20
, cw = Math.max(graphWidth, this.mtime * 20 + 20)
, ch = this.mspace * 20 + 100
, cw = Math.max(graphWidth, this.mtime * 20 + 260)
, r = Raphael(this.element.get(0), cw, ch)
, top = r.set()
, cuday = 0
, cumonth = ""
, offsetX = 20
, offsetY = 60
, barWidth = Math.max(graphWidth, this.dayCount * 20 + 80);
, barWidth = Math.max(graphWidth, this.dayCount * 20 + 320);
this.raphael = r;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment