Merge pull request #2746 from gitlabhq/features/teams
New feature: Teams
This commit is contained in:
commit
aa1f1eb680
|
@ -7,6 +7,7 @@
|
||||||
@extend .right;
|
@extend .right;
|
||||||
|
|
||||||
.groups_box,
|
.groups_box,
|
||||||
|
.teams_box,
|
||||||
.projects_box {
|
.projects_box {
|
||||||
> .title {
|
> .title {
|
||||||
padding: 2px 15px;
|
padding: 2px 15px;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Provides a base class for Admin controllers to subclass
|
# Provides a base class for Admin controllers to subclass
|
||||||
#
|
#
|
||||||
# Automatically sets the layout and ensures an administrator is logged in
|
# Automatically sets the layout and ensures an administrator is logged in
|
||||||
class AdminController < ApplicationController
|
class Admin::ApplicationController < ApplicationController
|
||||||
layout 'admin'
|
layout 'admin'
|
||||||
before_filter :authenticate_admin!
|
before_filter :authenticate_admin!
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class Admin::DashboardController < AdminController
|
class Admin::DashboardController < Admin::ApplicationController
|
||||||
def index
|
def index
|
||||||
@projects = Project.order("created_at DESC").limit(10)
|
@projects = Project.order("created_at DESC").limit(10)
|
||||||
@users = User.order("created_at DESC").limit(10)
|
@users = User.order("created_at DESC").limit(10)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class Admin::GroupsController < AdminController
|
class Admin::GroupsController < Admin::ApplicationController
|
||||||
before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update]
|
before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class Admin::HooksController < AdminController
|
class Admin::HooksController < Admin::ApplicationController
|
||||||
def index
|
def index
|
||||||
@hooks = SystemHook.all
|
@hooks = SystemHook.all
|
||||||
@hook = SystemHook.new
|
@hook = SystemHook.new
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
class Admin::LogsController < AdminController
|
class Admin::LogsController < Admin::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
11
app/controllers/admin/projects/application_controller.rb
Normal file
11
app/controllers/admin/projects/application_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Provides a base class for Admin controllers to subclass
|
||||||
|
#
|
||||||
|
# Automatically sets the layout and ensures an administrator is logged in
|
||||||
|
class Admin::Projects::ApplicationController < Admin::ApplicationController
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def project
|
||||||
|
@project ||= Project.find_by_path(params[:project_id])
|
||||||
|
end
|
||||||
|
end
|
32
app/controllers/admin/projects/members_controller.rb
Normal file
32
app/controllers/admin/projects/members_controller.rb
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
class Admin::Projects::MembersController < Admin::Projects::ApplicationController
|
||||||
|
def edit
|
||||||
|
@member = team_member
|
||||||
|
@project = project
|
||||||
|
@team_member_relation = team_member_relation
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
if team_member_relation.update_attributes(params[:team_member])
|
||||||
|
redirect_to [:admin, project], notice: 'Project Access was successfully updated.'
|
||||||
|
else
|
||||||
|
render action: "edit"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
team_member_relation.destroy
|
||||||
|
|
||||||
|
redirect_to :back
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def team_member
|
||||||
|
@member ||= project.users.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def team_member_relation
|
||||||
|
team_member.users_projects.find_by_project_id(project)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,4 +1,4 @@
|
||||||
class Admin::ProjectsController < AdminController
|
class Admin::ProjectsController < Admin::ApplicationController
|
||||||
before_filter :project, only: [:edit, :show, :update, :destroy, :team_update]
|
before_filter :project, only: [:edit, :show, :update, :destroy, :team_update]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@ -29,7 +29,9 @@ class Admin::ProjectsController < AdminController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
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
|
if status
|
||||||
redirect_to [:admin, @project], notice: 'Project was successfully updated.'
|
redirect_to [:admin, @project], notice: 'Project was successfully updated.'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class Admin::ResqueController < AdminController
|
class Admin::ResqueController < Admin::ApplicationController
|
||||||
def show
|
def show
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
class Admin::TeamMembersController < AdminController
|
|
||||||
def edit
|
|
||||||
@admin_team_member = UsersProject.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
@admin_team_member = UsersProject.find(params[:id])
|
|
||||||
|
|
||||||
if @admin_team_member.update_attributes(params[:team_member])
|
|
||||||
redirect_to [:admin, @admin_team_member.project], notice: 'Project Access was successfully updated.'
|
|
||||||
else
|
|
||||||
render action: "edit"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
@admin_team_member = UsersProject.find(params[:id])
|
|
||||||
@admin_team_member.destroy
|
|
||||||
|
|
||||||
redirect_to :back
|
|
||||||
end
|
|
||||||
end
|
|
11
app/controllers/admin/teams/application_controller.rb
Normal file
11
app/controllers/admin/teams/application_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Provides a base class for Admin controllers to subclass
|
||||||
|
#
|
||||||
|
# Automatically sets the layout and ensures an administrator is logged in
|
||||||
|
class Admin::Teams::ApplicationController < Admin::ApplicationController
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def user_team
|
||||||
|
@team = UserTeam.find_by_path(params[:team_id])
|
||||||
|
end
|
||||||
|
end
|
41
app/controllers/admin/teams/members_controller.rb
Normal file
41
app/controllers/admin/teams/members_controller.rb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
class Admin::Teams::MembersController < Admin::Teams::ApplicationController
|
||||||
|
def new
|
||||||
|
@users = User.potential_team_members(user_team)
|
||||||
|
@users = UserDecorator.decorate @users
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
unless params[:user_ids].blank?
|
||||||
|
user_ids = params[:user_ids]
|
||||||
|
access = params[:default_project_access]
|
||||||
|
is_admin = params[:group_admin]
|
||||||
|
user_team.add_members(user_ids, access, is_admin)
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_team_path(user_team), notice: 'Members was successfully added into Team of users.'
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
team_member
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]}
|
||||||
|
if user_team.update_membership(team_member, options)
|
||||||
|
redirect_to admin_team_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users."
|
||||||
|
else
|
||||||
|
render :edit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
user_team.remove_member(team_member)
|
||||||
|
redirect_to admin_team_path(user_team), notice: "Member #{team_member.name} was successfully removed from Team of users."
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def team_member
|
||||||
|
@member ||= user_team.members.find(params[:id])
|
||||||
|
end
|
||||||
|
end
|
41
app/controllers/admin/teams/projects_controller.rb
Normal file
41
app/controllers/admin/teams/projects_controller.rb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
class Admin::Teams::ProjectsController < Admin::Teams::ApplicationController
|
||||||
|
def new
|
||||||
|
@projects = Project.scoped
|
||||||
|
@projects = @projects.without_team(user_team) if user_team.projects.any?
|
||||||
|
#@projects.reject!(&:empty_repo?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
unless params[:project_ids].blank?
|
||||||
|
project_ids = params[:project_ids]
|
||||||
|
access = params[:greatest_project_access]
|
||||||
|
user_team.assign_to_projects(project_ids, access)
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_team_path(user_team), notice: 'Team of users was successfully assgned to projects.'
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
team_project
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
if user_team.update_project_access(team_project, params[:greatest_project_access])
|
||||||
|
redirect_to admin_team_path(user_team), notice: 'Access was successfully updated.'
|
||||||
|
else
|
||||||
|
render :edit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
user_team.resign_from_project(team_project)
|
||||||
|
redirect_to admin_team_path(user_team), notice: 'Team of users was successfully reassigned from project.'
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def team_project
|
||||||
|
@project ||= user_team.projects.find_with_namespace(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
59
app/controllers/admin/teams_controller.rb
Normal file
59
app/controllers/admin/teams_controller.rb
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
class Admin::TeamsController < Admin::ApplicationController
|
||||||
|
def index
|
||||||
|
@teams = UserTeam.order('name ASC')
|
||||||
|
@teams = @teams.search(params[:name]) if params[:name].present?
|
||||||
|
@teams = @teams.page(params[:page]).per(20)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
user_team
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@team = UserTeam.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
user_team
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@team = UserTeam.new(params[:user_team])
|
||||||
|
@team.path = @team.name.dup.parameterize if @team.name
|
||||||
|
@team.owner = current_user
|
||||||
|
|
||||||
|
if @team.save
|
||||||
|
redirect_to admin_team_path(@team), notice: 'Team of users was successfully created.'
|
||||||
|
else
|
||||||
|
render action: "new"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
user_team_params = params[:user_team].dup
|
||||||
|
owner_id = user_team_params.delete(:owner_id)
|
||||||
|
|
||||||
|
if owner_id
|
||||||
|
user_team.owner = User.find(owner_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if user_team.update_attributes(user_team_params)
|
||||||
|
redirect_to admin_team_path(user_team), notice: 'Team of users was successfully updated.'
|
||||||
|
else
|
||||||
|
render action: "edit"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
user_team.destroy
|
||||||
|
|
||||||
|
redirect_to admin_user_teams_path, notice: 'Team of users was successfully deleted.'
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def user_team
|
||||||
|
@team ||= UserTeam.find_by_path(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,4 +1,4 @@
|
||||||
class Admin::UsersController < AdminController
|
class Admin::UsersController < Admin::ApplicationController
|
||||||
def index
|
def index
|
||||||
@admin_users = User.scoped
|
@admin_users = User.scoped
|
||||||
@admin_users = @admin_users.filter(params[:filter])
|
@admin_users = @admin_users.filter(params[:filter])
|
||||||
|
|
|
@ -94,6 +94,14 @@ class ApplicationController < ActionController::Base
|
||||||
return access_denied! unless can?(current_user, :download_code, project)
|
return access_denied! unless can?(current_user, :download_code, project)
|
||||||
end
|
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!
|
def access_denied!
|
||||||
render "errors/access_denied", layout: "errors", status: 404
|
render "errors/access_denied", layout: "errors", status: 404
|
||||||
end
|
end
|
||||||
|
@ -135,4 +143,5 @@ class ApplicationController < ActionController::Base
|
||||||
def dev_tools
|
def dev_tools
|
||||||
Rack::MiniProfiler.authorize_request
|
Rack::MiniProfiler.authorize_request
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,8 @@ class DashboardController < ApplicationController
|
||||||
@projects
|
@projects
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@teams = (UserTeam.with_member(current_user) + UserTeam.created_by(current_user)).uniq
|
||||||
|
|
||||||
@projects = @projects.page(params[:page]).per(30)
|
@projects = @projects.page(params[:page]).per(30)
|
||||||
|
|
||||||
@events = Event.in_projects(current_user.authorized_projects.pluck(:id))
|
@events = Event.in_projects(current_user.authorized_projects.pluck(:id))
|
||||||
|
|
11
app/controllers/projects/application_controller.rb
Normal file
11
app/controllers/projects/application_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class Projects::ApplicationController < ApplicationController
|
||||||
|
|
||||||
|
before_filter :authorize_admin_team_member!
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def user_team
|
||||||
|
@team ||= UserTeam.find_by_path(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
27
app/controllers/projects/teams_controller.rb
Normal file
27
app/controllers/projects/teams_controller.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
class Projects::TeamsController < Projects::ApplicationController
|
||||||
|
|
||||||
|
def available
|
||||||
|
@teams = current_user.is_admin? ? UserTeam.scoped : current_user.user_teams
|
||||||
|
@teams = @teams.without_project(project)
|
||||||
|
unless @teams.any?
|
||||||
|
redirect_to project_team_index_path(project), notice: "No avaliable teams for assigment."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign
|
||||||
|
unless params[:team_id].blank?
|
||||||
|
team = UserTeam.find(params[:team_id])
|
||||||
|
access = params[:greatest_project_access]
|
||||||
|
team.assign_to_project(project, access)
|
||||||
|
end
|
||||||
|
redirect_to project_team_index_path(project)
|
||||||
|
end
|
||||||
|
|
||||||
|
def resign
|
||||||
|
team = project.user_teams.find_by_path(params[:id])
|
||||||
|
team.resign_from_project(project)
|
||||||
|
|
||||||
|
redirect_to project_team_index_path(project)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -19,7 +19,7 @@ class ProjectsController < ProjectResourceController
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@project = Projects::CreateContext.new(current_user, params[:project]).execute
|
@project = ::Projects::CreateContext.new(current_user, params[:project]).execute
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
flash[:notice] = 'Project was successfully created.' if @project.saved?
|
flash[:notice] = 'Project was successfully created.' if @project.saved?
|
||||||
|
@ -35,7 +35,7 @@ class ProjectsController < ProjectResourceController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
status = Projects::UpdateContext.new(project, current_user, params).execute
|
status = ::Projects::UpdateContext.new(project, current_user, params).execute
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if status
|
if status
|
||||||
|
|
|
@ -4,15 +4,16 @@ class TeamMembersController < ProjectResourceController
|
||||||
before_filter :authorize_admin_project!, except: [:index, :show]
|
before_filter :authorize_admin_project!, except: [:index, :show]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
@teams = UserTeam.scoped
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@team_member = project.users_projects.find(params[:id])
|
@user_project_relation = project.users_projects.find_by_user_id(member)
|
||||||
@events = @team_member.user.recent_events.where(:project_id => @project.id).limit(7)
|
@events = member.recent_events.in_projects(project).limit(7)
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@team_member = project.users_projects.new
|
@user_project_relation = project.users_projects.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -28,18 +29,18 @@ class TeamMembersController < ProjectResourceController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@team_member = project.users_projects.find(params[:id])
|
@user_project_relation = project.users_projects.find_by_user_id(member)
|
||||||
@team_member.update_attributes(params[:team_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"
|
flash[:alert] = "User should have at least one role"
|
||||||
end
|
end
|
||||||
redirect_to project_team_index_path(@project)
|
redirect_to project_team_index_path(@project)
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@team_member = project.users_projects.find(params[:id])
|
@user_project_relation = project.users_projects.find_by_user_id(params[:id])
|
||||||
@team_member.destroy
|
@user_project_relation.destroy
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to project_team_index_path(@project) }
|
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
|
redirect_to project_team_members_path(project), notice: notice
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def member
|
||||||
|
@member ||= User.find(params[:id])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
13
app/controllers/teams/application_controller.rb
Normal file
13
app/controllers/teams/application_controller.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
class Teams::ApplicationController < ApplicationController
|
||||||
|
|
||||||
|
layout 'user_team'
|
||||||
|
|
||||||
|
before_filter :authorize_manage_user_team!
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def user_team
|
||||||
|
@team ||= UserTeam.find_by_path(params[:team_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
49
app/controllers/teams/members_controller.rb
Normal file
49
app/controllers/teams/members_controller.rb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
class Teams::MembersController < Teams::ApplicationController
|
||||||
|
|
||||||
|
skip_before_filter :authorize_manage_user_team!, only: [:index]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@members = user_team.members
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@users = User.potential_team_members(user_team)
|
||||||
|
@users = UserDecorator.decorate @users
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
unless params[:user_ids].blank?
|
||||||
|
user_ids = params[:user_ids]
|
||||||
|
access = params[:default_project_access]
|
||||||
|
is_admin = params[:group_admin]
|
||||||
|
user_team.add_members(user_ids, access, is_admin)
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to team_members_path(user_team), notice: 'Members was successfully added into Team of users.'
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
team_member
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]}
|
||||||
|
if user_team.update_membership(team_member, options)
|
||||||
|
redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users."
|
||||||
|
else
|
||||||
|
render :edit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
user_team.remove_member(team_member)
|
||||||
|
redirect_to team_path(user_team), notice: "Member #{team_member.name} was successfully removed from Team of users."
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def team_member
|
||||||
|
@member ||= user_team.members.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
57
app/controllers/teams/projects_controller.rb
Normal file
57
app/controllers/teams/projects_controller.rb
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
class Teams::ProjectsController < Teams::ApplicationController
|
||||||
|
|
||||||
|
skip_before_filter :authorize_manage_user_team!, only: [:index]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@projects = user_team.projects
|
||||||
|
@avaliable_projects = current_user.admin? ? Project.without_team(user_team) : current_user.owned_projects.without_team(user_team)
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
user_team
|
||||||
|
@avaliable_projects = current_user.owned_projects.scoped
|
||||||
|
@avaliable_projects = @avaliable_projects.without_team(user_team) if user_team.projects.any?
|
||||||
|
|
||||||
|
redirect_to team_projects_path(user_team), notice: "No avalible projects." unless @avaliable_projects.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
redirect_to :back if params[:project_ids].blank?
|
||||||
|
|
||||||
|
project_ids = params[:project_ids]
|
||||||
|
access = params[:greatest_project_access]
|
||||||
|
|
||||||
|
# Reject non-allowed projects
|
||||||
|
allowed_project_ids = current_user.owned_projects.map(&:id)
|
||||||
|
project_ids.select! { |id| allowed_project_ids.include?(id) }
|
||||||
|
|
||||||
|
# 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
|
92
app/controllers/teams_controller.rb
Normal file
92
app/controllers/teams_controller.rb
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
class TeamsController < ApplicationController
|
||||||
|
# Authorize
|
||||||
|
before_filter :authorize_manage_user_team!
|
||||||
|
before_filter :authorize_admin_user_team!
|
||||||
|
|
||||||
|
# Skip access control on public section
|
||||||
|
skip_before_filter :authorize_manage_user_team!, only: [:index, :show, :new, :destroy, :create, :search, :issues, :merge_requests]
|
||||||
|
skip_before_filter :authorize_admin_user_team!, only: [:index, :show, :new, :create, :search, :issues, :merge_requests]
|
||||||
|
|
||||||
|
layout 'user_team', only: [:show, :edit, :update, :destroy, :issues, :merge_requests, :search]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@teams = current_user.user_teams.order('name ASC')
|
||||||
|
end
|
||||||
|
|
||||||
|
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 teams_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
|
||||||
|
|
||||||
|
def search
|
||||||
|
result = SearchContext.new(user_team.project_ids, params).execute
|
||||||
|
|
||||||
|
@projects = result[:projects]
|
||||||
|
@merge_requests = result[:merge_requests]
|
||||||
|
@issues = result[:issues]
|
||||||
|
@wiki_pages = result[:wiki_pages]
|
||||||
|
@teams = result[:teams]
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def projects
|
||||||
|
@projects ||= user_team.projects.sorted_by_activity
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_team
|
||||||
|
@team ||= UserTeam.find_by_path(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -8,4 +8,8 @@ class UserDecorator < ApplicationDecorator
|
||||||
def tm_of(project)
|
def tm_of(project)
|
||||||
project.team_member_by_id(self.id)
|
project.team_member_by_id(self.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def name_with_email
|
||||||
|
"#{name} (#{email})"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
5
app/helpers/admin/teams/members_helper.rb
Normal file
5
app/helpers/admin/teams/members_helper.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module Admin::Teams::MembersHelper
|
||||||
|
def member_since(team, member)
|
||||||
|
team.user_team_user_relationships.find_by_user_id(member).created_at
|
||||||
|
end
|
||||||
|
end
|
5
app/helpers/admin/teams/projects_helper.rb
Normal file
5
app/helpers/admin/teams/projects_helper.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module Admin::Teams::ProjectsHelper
|
||||||
|
def assigned_since(team, project)
|
||||||
|
team.user_team_project_relationships.find_by_project_id(project).created_at
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,8 +3,12 @@ module ProjectsHelper
|
||||||
@project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
|
@project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_from_team_message(project, member)
|
def grouper_project_teams(project)
|
||||||
"You are going to remove #{member.user_name} from #{project.name}. Are you sure?"
|
@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
|
end
|
||||||
|
|
||||||
def link_to_project project
|
def link_to_project project
|
||||||
|
|
26
app/helpers/user_teams_helper.rb
Normal file
26
app/helpers/user_teams_helper.rb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module UserTeamsHelper
|
||||||
|
def team_filter_path(entity, options={})
|
||||||
|
exist_opts = {
|
||||||
|
status: params[:status],
|
||||||
|
project_id: params[:project_id],
|
||||||
|
}
|
||||||
|
|
||||||
|
options = exist_opts.merge(options)
|
||||||
|
|
||||||
|
case entity
|
||||||
|
when 'issue' then
|
||||||
|
issues_team_path(@team, options)
|
||||||
|
when 'merge_request'
|
||||||
|
merge_requests_team_path(@team, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def grouped_user_team_members(team)
|
||||||
|
team.user_team_user_relationships.sort_by(&:permission).reverse.group_by(&:permission)
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_from_user_team_message(team, member)
|
||||||
|
"You are going to remove #{member.name} from #{team.name}. Are you sure?"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -8,6 +8,7 @@ class Ability
|
||||||
when "Snippet" then snippet_abilities(object, subject)
|
when "Snippet" then snippet_abilities(object, subject)
|
||||||
when "MergeRequest" then merge_request_abilities(object, subject)
|
when "MergeRequest" then merge_request_abilities(object, subject)
|
||||||
when "Group", "Namespace" then group_abilities(object, subject)
|
when "Group", "Namespace" then group_abilities(object, subject)
|
||||||
|
when "UserTeam" then user_team_abilities(object, subject)
|
||||||
else []
|
else []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -110,6 +111,22 @@ class Ability
|
||||||
rules.flatten
|
rules.flatten
|
||||||
end
|
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|
|
[:issue, :note, :snippet, :merge_request].each do |name|
|
||||||
define_method "#{name}_abilities" do |user, subject|
|
define_method "#{name}_abilities" do |user, subject|
|
||||||
if subject.author == user
|
if subject.author == user
|
||||||
|
|
|
@ -22,6 +22,7 @@ module Issuable
|
||||||
scope :opened, where(closed: false)
|
scope :opened, where(closed: false)
|
||||||
scope :closed, where(closed: true)
|
scope :closed, where(closed: true)
|
||||||
scope :of_group, ->(group) { where(project_id: group.project_ids) }
|
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 :assigned, ->(u) { where(assignee_id: u.id)}
|
||||||
scope :recent, order("created_at DESC")
|
scope :recent, order("created_at DESC")
|
||||||
|
|
||||||
|
|
|
@ -33,14 +33,13 @@ class Project < ActiveRecord::Base
|
||||||
attr_accessor :error_code
|
attr_accessor :error_code
|
||||||
|
|
||||||
# Relations
|
# Relations
|
||||||
|
belongs_to :creator, foreign_key: "creator_id", class_name: "User"
|
||||||
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
|
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
|
||||||
belongs_to :namespace
|
belongs_to :namespace
|
||||||
|
|
||||||
belongs_to :creator,
|
has_one :last_event, class_name: 'Event', order: 'events.created_at DESC', foreign_key: 'project_id'
|
||||||
class_name: "User",
|
has_one :gitlab_ci_service, dependent: :destroy
|
||||||
foreign_key: "creator_id"
|
|
||||||
|
|
||||||
has_many :users, through: :users_projects
|
|
||||||
has_many :events, dependent: :destroy
|
has_many :events, dependent: :destroy
|
||||||
has_many :merge_requests, dependent: :destroy
|
has_many :merge_requests, dependent: :destroy
|
||||||
has_many :issues, dependent: :destroy, order: "closed, created_at DESC"
|
has_many :issues, dependent: :destroy, order: "closed, created_at DESC"
|
||||||
|
@ -48,12 +47,16 @@ class Project < ActiveRecord::Base
|
||||||
has_many :users_projects, dependent: :destroy
|
has_many :users_projects, dependent: :destroy
|
||||||
has_many :notes, dependent: :destroy
|
has_many :notes, dependent: :destroy
|
||||||
has_many :snippets, dependent: :destroy
|
has_many :snippets, dependent: :destroy
|
||||||
has_many :deploy_keys, dependent: :destroy, foreign_key: "project_id", class_name: "Key"
|
has_many :deploy_keys, dependent: :destroy, class_name: "Key", foreign_key: "project_id"
|
||||||
has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
|
has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
|
||||||
has_many :wikis, dependent: :destroy
|
has_many :wikis, dependent: :destroy
|
||||||
has_many :protected_branches, 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_many :user_team_project_relationships, dependent: :destroy
|
||||||
has_one :gitlab_ci_service, 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
|
delegate :name, to: :owner, allow_nil: true, prefix: true
|
||||||
|
|
||||||
|
@ -77,6 +80,8 @@ class Project < ActiveRecord::Base
|
||||||
# Scopes
|
# Scopes
|
||||||
scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
|
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 :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) }
|
||||||
|
scope :without_team, ->(team) { where("id NOT IN (:ids)", ids: team.projects.map(&:id)) }
|
||||||
|
scope :in_team, ->(team) { where("id IN (:ids)", ids: team.projects.map(&:id)) }
|
||||||
scope :in_namespace, ->(namespace) { where(namespace_id: namespace.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 :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) }
|
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
|
||||||
|
@ -122,7 +127,7 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def team
|
def team
|
||||||
@team ||= Team.new(self)
|
@team ||= ProjectTeam.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def repository
|
def repository
|
||||||
|
@ -489,6 +494,11 @@ class Project < ActiveRecord::Base
|
||||||
http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
|
http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
|
||||||
end
|
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
|
# Check if current branch name is marked as protected in the system
|
||||||
def protected_branch? branch_name
|
def protected_branch? branch_name
|
||||||
protected_branches.map(&:name).include?(branch_name)
|
protected_branches.map(&:name).include?(branch_name)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class Team
|
class ProjectTeam
|
||||||
attr_accessor :project
|
attr_accessor :project
|
||||||
|
|
||||||
def initialize(project)
|
def initialize(project)
|
|
@ -45,18 +45,27 @@ class User < ActiveRecord::Base
|
||||||
attr_accessor :force_random_password
|
attr_accessor :force_random_password
|
||||||
|
|
||||||
# Namespace for personal projects
|
# Namespace for personal projects
|
||||||
has_one :namespace, class_name: "Namespace", foreign_key: :owner_id, conditions: 'type IS NULL', dependent: :destroy
|
has_one :namespace, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace", conditions: 'type IS NULL'
|
||||||
has_many :groups, class_name: "Group", foreign_key: :owner_id
|
|
||||||
|
|
||||||
has_many :keys, dependent: :destroy
|
has_many :keys, dependent: :destroy
|
||||||
has_many :users_projects, dependent: :destroy
|
has_many :users_projects, dependent: :destroy
|
||||||
has_many :issues, foreign_key: :author_id, dependent: :destroy
|
has_many :issues, dependent: :destroy, foreign_key: :author_id
|
||||||
has_many :notes, foreign_key: :author_id, dependent: :destroy
|
has_many :notes, dependent: :destroy, foreign_key: :author_id
|
||||||
has_many :merge_requests, foreign_key: :author_id, dependent: :destroy
|
has_many :merge_requests, dependent: :destroy, foreign_key: :author_id
|
||||||
has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy
|
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 :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"
|
||||||
has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy
|
|
||||||
has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy
|
has_many :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 :name, presence: true
|
||||||
validates :bio, length: { within: 0..255 }
|
validates :bio, length: { within: 0..255 }
|
||||||
|
@ -80,6 +89,9 @@ class User < ActiveRecord::Base
|
||||||
scope :blocked, where(blocked: true)
|
scope :blocked, where(blocked: true)
|
||||||
scope :active, where(blocked: false)
|
scope :active, where(blocked: false)
|
||||||
scope :alphabetically, order('name ASC')
|
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 : active.not_in_team(team) }
|
||||||
|
|
||||||
#
|
#
|
||||||
# Class methods
|
# Class methods
|
||||||
|
|
97
app/models/user_team.rb
Normal file
97
app/models/user_team.rb
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
class UserTeam < ActiveRecord::Base
|
||||||
|
attr_accessible :name, :owner_id, :path
|
||||||
|
|
||||||
|
belongs_to :owner, class_name: User
|
||||||
|
|
||||||
|
has_many :user_team_project_relationships, dependent: :destroy
|
||||||
|
has_many :user_team_user_relationships, dependent: :destroy
|
||||||
|
|
||||||
|
has_many :projects, through: :user_team_project_relationships
|
||||||
|
has_many :members, through: :user_team_user_relationships, source: :user
|
||||||
|
|
||||||
|
validates :name, presence: true, uniqueness: true
|
||||||
|
validates :owner, presence: true
|
||||||
|
validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
|
||||||
|
format: { with: Gitlab::Regex.path_regex,
|
||||||
|
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
|
||||||
|
|
||||||
|
scope :with_member, ->(user){ joins(:user_team_user_relationships).where(user_team_user_relationships: {user_id: user.id}) }
|
||||||
|
scope :with_project, ->(project){ joins(:user_team_project_relationships).where(user_team_project_relationships: {project_id: project})}
|
||||||
|
scope :without_project, ->(project){ where("user_teams.id NOT IN (:ids)", ids: (a = with_project(project); a.blank? ? 0 : a))}
|
||||||
|
scope :created_by, ->(user){ where(owner_id: user) }
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def search query
|
||||||
|
where("name LIKE :query OR path LIKE :query", query: "%#{query}%")
|
||||||
|
end
|
||||||
|
|
||||||
|
def global_id
|
||||||
|
'GLN'
|
||||||
|
end
|
||||||
|
|
||||||
|
def access_roles
|
||||||
|
UsersProject.access_roles
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_param
|
||||||
|
path
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign_to_projects(projects, access)
|
||||||
|
projects.each do |project|
|
||||||
|
assign_to_project(project, access)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign_to_project(project, access)
|
||||||
|
Gitlab::UserTeamManager.assign(self, project, access)
|
||||||
|
end
|
||||||
|
|
||||||
|
def resign_from_project(project)
|
||||||
|
Gitlab::UserTeamManager.resign(self, project)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_members(users, access, group_admin)
|
||||||
|
users.each do |user|
|
||||||
|
add_member(user, access, group_admin)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_member(user, access, group_admin)
|
||||||
|
Gitlab::UserTeamManager.add_member_into_team(self, user, access, group_admin)
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_member(user)
|
||||||
|
Gitlab::UserTeamManager.remove_member_from_team(self, user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_membership(user, options)
|
||||||
|
Gitlab::UserTeamManager.update_team_user_membership(self, user, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_project_access(project, permission)
|
||||||
|
Gitlab::UserTeamManager.update_project_greates_access(self, project, permission)
|
||||||
|
end
|
||||||
|
|
||||||
|
def max_project_access(project)
|
||||||
|
user_team_project_relationships.find_by_project_id(project).greatest_access
|
||||||
|
end
|
||||||
|
|
||||||
|
def human_max_project_access(project)
|
||||||
|
self.class.access_roles.invert[max_project_access(project)]
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_projects_access(member)
|
||||||
|
user_team_user_relationships.find_by_user_id(member).permission
|
||||||
|
end
|
||||||
|
|
||||||
|
def human_default_projects_access(member)
|
||||||
|
self.class.access_roles.invert[default_projects_access(member)]
|
||||||
|
end
|
||||||
|
|
||||||
|
def admin?(member)
|
||||||
|
user_team_user_relationships.with_user(member).first.group_admin?
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
28
app/models/user_team_project_relationship.rb
Normal file
28
app/models/user_team_project_relationship.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class UserTeamProjectRelationship < ActiveRecord::Base
|
||||||
|
attr_accessible :greatest_access, :project_id, :user_team_id
|
||||||
|
|
||||||
|
belongs_to :user_team
|
||||||
|
belongs_to :project
|
||||||
|
|
||||||
|
validates :project, presence: true
|
||||||
|
validates :user_team, presence: true
|
||||||
|
validate :check_greatest_access
|
||||||
|
|
||||||
|
scope :with_project, ->(project){ where(project_id: project.id) }
|
||||||
|
|
||||||
|
def team_name
|
||||||
|
user_team.name
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_greatest_access
|
||||||
|
errors.add(:base, :incorrect_access_code) unless correct_access?
|
||||||
|
end
|
||||||
|
|
||||||
|
def correct_access?
|
||||||
|
return false if greatest_access.blank?
|
||||||
|
return true if UsersProject.access_roles.has_value?(greatest_access)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
19
app/models/user_team_user_relationship.rb
Normal file
19
app/models/user_team_user_relationship.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
class UserTeamUserRelationship < ActiveRecord::Base
|
||||||
|
attr_accessible :group_admin, :permission, :user_id, :user_team_id
|
||||||
|
|
||||||
|
belongs_to :user_team
|
||||||
|
belongs_to :user
|
||||||
|
|
||||||
|
validates :user_team, presence: true
|
||||||
|
validates :user, presence: true
|
||||||
|
|
||||||
|
scope :with_user, ->(user) { where(user_id: user.id) }
|
||||||
|
|
||||||
|
def user_name
|
||||||
|
user.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def access_human
|
||||||
|
UsersProject.access_roles.invert[permission]
|
||||||
|
end
|
||||||
|
end
|
|
@ -39,7 +39,10 @@ class UsersProject < ActiveRecord::Base
|
||||||
scope :reporters, where(project_access: REPORTER)
|
scope :reporters, where(project_access: REPORTER)
|
||||||
scope :developers, where(project_access: DEVELOPER)
|
scope :developers, where(project_access: DEVELOPER)
|
||||||
scope :masters, where(project_access: MASTER)
|
scope :masters, where(project_access: MASTER)
|
||||||
|
|
||||||
scope :in_project, ->(project) { where(project_id: project.id) }
|
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
|
class << self
|
||||||
|
|
||||||
|
|
16
app/views/admin/projects/members/_form.html.haml
Normal file
16
app/views/admin/projects/members/_form.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
= form_for @team_member_relation, as: :team_member, url: admin_project_member_path(@project, @member) do |f|
|
||||||
|
-if @team_member_relation.errors.any?
|
||||||
|
.alert-message.block-message.error
|
||||||
|
%ul
|
||||||
|
- @team_member_relation.errors.full_messages.each do |msg|
|
||||||
|
%li= msg
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
%label Project Access:
|
||||||
|
.input
|
||||||
|
= f.select :project_access, options_for_select(Project.access_options, @team_member_relation.project_access), {}, class: "project-access-select chosen span3"
|
||||||
|
|
||||||
|
%br
|
||||||
|
.actions
|
||||||
|
= f.submit 'Save', class: "btn primary"
|
||||||
|
= link_to 'Cancel', :back, class: "btn"
|
8
app/views/admin/projects/members/edit.html.haml
Normal file
8
app/views/admin/projects/members/edit.html.haml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
%p.slead
|
||||||
|
Edit access for
|
||||||
|
= link_to @member.name, admin_user_path(@member)
|
||||||
|
in
|
||||||
|
= link_to @project.name_with_namespace, admin_project_path(@project)
|
||||||
|
|
||||||
|
%hr
|
||||||
|
= render 'form'
|
|
@ -114,9 +114,9 @@
|
||||||
%h5
|
%h5
|
||||||
Team
|
Team
|
||||||
%small
|
%small
|
||||||
(#{@project.users_projects.count})
|
(#{@project.users.count})
|
||||||
%br
|
%br
|
||||||
%table.zebra-striped
|
%table.zebra-striped.team_members
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th Name
|
%th Name
|
||||||
|
@ -124,13 +124,13 @@
|
||||||
%th Repository Access
|
%th Repository Access
|
||||||
%th
|
%th
|
||||||
|
|
||||||
- @project.users_projects.each do |tm|
|
- @project.users.each do |tm|
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
= link_to tm.user_name, admin_user_path(tm.user)
|
= link_to tm.name, admin_user_path(tm)
|
||||||
%td= tm.project_access_human
|
%td= @project.project_access_human(tm)
|
||||||
%td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
|
%td= link_to 'Edit Access', edit_admin_project_member_path(@project, 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"
|
%td= link_to 'Remove from team', admin_project_member_path(@project, tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small"
|
||||||
|
|
||||||
%br
|
%br
|
||||||
%h5 Add new team member
|
%h5 Add new team member
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
= form_for @admin_team_member, as: :team_member, url: admin_team_member_path(@admin_team_member) do |f|
|
|
||||||
-if @admin_team_member.errors.any?
|
|
||||||
.alert-message.block-message.error
|
|
||||||
%ul
|
|
||||||
- @admin_team_member.errors.full_messages.each do |msg|
|
|
||||||
%li= msg
|
|
||||||
|
|
||||||
.clearfix
|
|
||||||
%label Project Access:
|
|
||||||
.input
|
|
||||||
= f.select :project_access, options_for_select(Project.access_options, @admin_team_member.project_access), {}, class: "project-access-select chosen span3"
|
|
||||||
|
|
||||||
%br
|
|
||||||
.actions
|
|
||||||
= f.submit 'Save', class: "btn primary"
|
|
||||||
= link_to 'Cancel', :back, class: "btn"
|
|
|
@ -1,8 +0,0 @@
|
||||||
%p.slead
|
|
||||||
Edit access for
|
|
||||||
= link_to @admin_team_member.user_name, admin_user_path(@admin_team_member)
|
|
||||||
in
|
|
||||||
= link_to @admin_team_member.project.name_with_namespace, admin_project_path(@admin_team_member)
|
|
||||||
|
|
||||||
%hr
|
|
||||||
= render 'form'
|
|
23
app/views/admin/teams/edit.html.haml
Normal file
23
app/views/admin/teams/edit.html.haml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
%h3.page_title Rename Team
|
||||||
|
%hr
|
||||||
|
= form_for @team, url: admin_team_path(@team), method: :put do |f|
|
||||||
|
- if @team.errors.any?
|
||||||
|
.alert-message.block-message.error
|
||||||
|
%span= @team.errors.full_messages.first
|
||||||
|
.clearfix.team_name_holder
|
||||||
|
= f.label :name do
|
||||||
|
Team name is
|
||||||
|
.input
|
||||||
|
= f.text_field :name, placeholder: "Example Team", class: "xxlarge"
|
||||||
|
|
||||||
|
.clearfix.team_name_holder
|
||||||
|
= f.label :path do
|
||||||
|
%span.cred Team path is
|
||||||
|
.input
|
||||||
|
= f.text_field :path, placeholder: "example-team", class: "xxlarge danger"
|
||||||
|
%ul.cred
|
||||||
|
%li It will change web url for access team and team projects.
|
||||||
|
|
||||||
|
.form-actions
|
||||||
|
= f.submit 'Rename team', class: "btn danger"
|
||||||
|
= link_to 'Cancel', admin_teams_path, class: "btn cancel-btn"
|
38
app/views/admin/teams/index.html.haml
Normal file
38
app/views/admin/teams/index.html.haml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
%h3.page_title
|
||||||
|
Teams
|
||||||
|
%small
|
||||||
|
simple Teams description
|
||||||
|
|
||||||
|
= link_to 'New Team', new_admin_team_path, class: "btn small right"
|
||||||
|
%br
|
||||||
|
|
||||||
|
= form_tag admin_teams_path, method: :get, class: 'form-inline' do
|
||||||
|
= text_field_tag :name, params[:name], class: "xlarge"
|
||||||
|
= submit_tag "Search", class: "btn submit primary"
|
||||||
|
|
||||||
|
%table
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th
|
||||||
|
Name
|
||||||
|
%i.icon-sort-down
|
||||||
|
%th Path
|
||||||
|
%th Projects
|
||||||
|
%th Members
|
||||||
|
%th Owner
|
||||||
|
%th.cred Danger Zone!
|
||||||
|
|
||||||
|
- @teams.each do |team|
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
%strong= link_to team.name, admin_team_path(team)
|
||||||
|
%td= team.path
|
||||||
|
%td= team.projects.count
|
||||||
|
%td= team.members.count
|
||||||
|
%td
|
||||||
|
= link_to team.owner.name, admin_user_path(team.owner_id)
|
||||||
|
%td.bgred
|
||||||
|
= link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn small"
|
||||||
|
= link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn small danger"
|
||||||
|
|
||||||
|
= paginate @teams, theme: "admin"
|
20
app/views/admin/teams/members/_form.html.haml
Normal file
20
app/views/admin/teams/members/_form.html.haml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
= form_tag admin_team_member_path(@team, @member), method: :put do
|
||||||
|
-if @member.errors.any?
|
||||||
|
.alert-message.block-message.error
|
||||||
|
%ul
|
||||||
|
- @member.errors.full_messages.each do |msg|
|
||||||
|
%li= msg
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
%label Default access for Team projects:
|
||||||
|
.input
|
||||||
|
= select_tag :default_project_access, options_for_select(UserTeam.access_roles, @team.default_projects_access(@member)), class: "project-access-select chosen span3"
|
||||||
|
.clearfix
|
||||||
|
%label Team admin?
|
||||||
|
.input
|
||||||
|
= check_box_tag :group_admin, true, @team.admin?(@member)
|
||||||
|
|
||||||
|
%br
|
||||||
|
.actions
|
||||||
|
= submit_tag 'Save', class: "btn primary"
|
||||||
|
= link_to 'Cancel', :back, class: "btn"
|
16
app/views/admin/teams/members/edit.html.haml
Normal file
16
app/views/admin/teams/members/edit.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
%h3
|
||||||
|
Edit access #{@member.name} in #{@team.name} team
|
||||||
|
|
||||||
|
%hr
|
||||||
|
%table.zebra-striped
|
||||||
|
%tr
|
||||||
|
%td User:
|
||||||
|
%td= @member.name
|
||||||
|
%tr
|
||||||
|
%td Team:
|
||||||
|
%td= @team.name
|
||||||
|
%tr
|
||||||
|
%td Since:
|
||||||
|
%td= member_since(@team, @member).stamp("Nov 11, 2010")
|
||||||
|
|
||||||
|
= render 'form'
|
29
app/views/admin/teams/members/new.html.haml
Normal file
29
app/views/admin/teams/members/new.html.haml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
%h3.page_title
|
||||||
|
Team: #{@team.name}
|
||||||
|
|
||||||
|
%fieldset
|
||||||
|
%legend Members (#{@team.members.count})
|
||||||
|
= form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do
|
||||||
|
%table#members_list
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th User name
|
||||||
|
%th Default project access
|
||||||
|
%th Team access
|
||||||
|
%th
|
||||||
|
- @team.members.each do |member|
|
||||||
|
%tr.member
|
||||||
|
%td
|
||||||
|
= link_to [:admin, member] do
|
||||||
|
= member.name
|
||||||
|
%small= "(#{member.email})"
|
||||||
|
%td= @team.human_default_projects_access(member)
|
||||||
|
%td= @team.admin?(member) ? "Admin" : "Member"
|
||||||
|
%td
|
||||||
|
%tr
|
||||||
|
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_email), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
|
||||||
|
%td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
|
||||||
|
%td
|
||||||
|
%span= check_box_tag :group_admin
|
||||||
|
%span Admin?
|
||||||
|
%td= submit_tag 'Add', class: "btn primary", id: :add_members_to_team
|
19
app/views/admin/teams/new.html.haml
Normal file
19
app/views/admin/teams/new.html.haml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
%h3.page_title New Team
|
||||||
|
%hr
|
||||||
|
= form_for @team, url: admin_teams_path do |f|
|
||||||
|
- if @team.errors.any?
|
||||||
|
.alert-message.block-message.error
|
||||||
|
%span= @team.errors.full_messages.first
|
||||||
|
.clearfix
|
||||||
|
= f.label :name do
|
||||||
|
Team name is
|
||||||
|
.input
|
||||||
|
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
|
||||||
|
|
||||||
|
= f.submit 'Create team', class: "btn primary"
|
||||||
|
%hr
|
||||||
|
.padded
|
||||||
|
%ul
|
||||||
|
%li All created teams are public (users can view who enter into team and which project are assigned for this team)
|
||||||
|
%li People within a team see only projects they have access to
|
||||||
|
%li You will be able to assign existing projects for team
|
16
app/views/admin/teams/projects/_form.html.haml
Normal file
16
app/views/admin/teams/projects/_form.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
= form_tag admin_team_project_path(@team, @project), method: :put do
|
||||||
|
-if @project.errors.any?
|
||||||
|
.alert-message.block-message.error
|
||||||
|
%ul
|
||||||
|
- @project.errors.full_messages.each do |msg|
|
||||||
|
%li= msg
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
%label Max access for Team members:
|
||||||
|
.input
|
||||||
|
= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles, @team.max_project_access(@project)), class: "project-access-select chosen span3"
|
||||||
|
|
||||||
|
%br
|
||||||
|
.actions
|
||||||
|
= submit_tag 'Save', class: "btn primary"
|
||||||
|
= link_to 'Cancel', :back, class: "btn"
|
16
app/views/admin/teams/projects/edit.html.haml
Normal file
16
app/views/admin/teams/projects/edit.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
%h3
|
||||||
|
Edit max access in #{@project.name} for #{@team.name} team
|
||||||
|
|
||||||
|
%hr
|
||||||
|
%table.zebra-striped
|
||||||
|
%tr
|
||||||
|
%td Project:
|
||||||
|
%td= @project.name
|
||||||
|
%tr
|
||||||
|
%td Team:
|
||||||
|
%td= @team.name
|
||||||
|
%tr
|
||||||
|
%td Since:
|
||||||
|
%td= assigned_since(@team, @project).stamp("Nov 11, 2010")
|
||||||
|
|
||||||
|
= render 'form'
|
23
app/views/admin/teams/projects/new.html.haml
Normal file
23
app/views/admin/teams/projects/new.html.haml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
%h3.page_title
|
||||||
|
Team: #{@team.name}
|
||||||
|
|
||||||
|
%fieldset
|
||||||
|
%legend Projects (#{@team.projects.count})
|
||||||
|
= form_tag admin_team_projects_path(@team), id: "assign_projects", class: "bulk_import", method: :post do
|
||||||
|
%table#projects_list
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th Project name
|
||||||
|
%th Max access
|
||||||
|
%th
|
||||||
|
- @team.projects.each do |project|
|
||||||
|
%tr.project
|
||||||
|
%td
|
||||||
|
= link_to project.name_with_namespace, [:admin, project]
|
||||||
|
%td
|
||||||
|
%span= @team.human_max_project_access(project)
|
||||||
|
%td
|
||||||
|
%tr
|
||||||
|
%td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
|
||||||
|
%td= select_tag :greatest_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
|
||||||
|
%td= submit_tag 'Add', class: "btn primary", id: :assign_projects_to_team
|
101
app/views/admin/teams/show.html.haml
Normal file
101
app/views/admin/teams/show.html.haml
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
%h3.page_title
|
||||||
|
Team: #{@team.name}
|
||||||
|
|
||||||
|
%br
|
||||||
|
%table.zebra-striped
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th Team
|
||||||
|
%th
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
%b
|
||||||
|
Name:
|
||||||
|
%td
|
||||||
|
= @team.name
|
||||||
|
|
||||||
|
= link_to edit_admin_team_path(@team), class: "btn btn-small right" do
|
||||||
|
%i.icon-edit
|
||||||
|
Rename
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
%b
|
||||||
|
Owner:
|
||||||
|
%td
|
||||||
|
= @team.owner.name
|
||||||
|
.right
|
||||||
|
= link_to "#", class: "btn btn-small change-owner-link" do
|
||||||
|
%i.icon-edit
|
||||||
|
Change owner
|
||||||
|
|
||||||
|
%tr.change-owner-holder.hide
|
||||||
|
%td.bgred
|
||||||
|
%b.cred
|
||||||
|
New Owner:
|
||||||
|
%td.bgred
|
||||||
|
= form_for @team, url: admin_team_path(@team) do |f|
|
||||||
|
= f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
|
||||||
|
%div
|
||||||
|
= f.submit 'Change Owner', class: "btn danger"
|
||||||
|
= link_to "Cancel", "#", class: "btn change-owner-cancel-link"
|
||||||
|
|
||||||
|
%fieldset
|
||||||
|
%legend
|
||||||
|
Members (#{@team.members.count})
|
||||||
|
%span= link_to 'Add members', new_admin_team_member_path(@team), class: "btn success small right", id: :add_members_to_team
|
||||||
|
- if @team.members.any?
|
||||||
|
%table#members_list
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th User name
|
||||||
|
%th Default project access
|
||||||
|
%th Team access
|
||||||
|
%th.cred.span3 Danger Zone!
|
||||||
|
- @team.members.each do |member|
|
||||||
|
%tr.member{ class: "user_#{member.id}"}
|
||||||
|
%td
|
||||||
|
= link_to [:admin, member] do
|
||||||
|
= member.name
|
||||||
|
%small= "(#{member.email})"
|
||||||
|
%td= @team.human_default_projects_access(member)
|
||||||
|
%td= @team.admin?(member) ? "Admin" : "Member"
|
||||||
|
%td.bgred
|
||||||
|
= link_to 'Edit', edit_admin_team_member_path(@team, member), class: "btn small"
|
||||||
|
|
||||||
|
= link_to 'Remove', admin_team_member_path(@team, member), confirm: 'Remove member from team. Are you sure?', method: :delete, class: "btn danger small", id: "remove_member_#{member.id}"
|
||||||
|
|
||||||
|
%fieldset
|
||||||
|
%legend
|
||||||
|
Projects (#{@team.projects.count})
|
||||||
|
%span= link_to 'Add projects', new_admin_team_project_path(@team), class: "btn success small right", id: :assign_projects_to_team
|
||||||
|
- if @team.projects.any?
|
||||||
|
%table#projects_list
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th Project name
|
||||||
|
%th Max access
|
||||||
|
%th.cred.span3 Danger Zone!
|
||||||
|
- @team.projects.each do |project|
|
||||||
|
%tr.project
|
||||||
|
%td
|
||||||
|
= link_to project.name_with_namespace, [:admin, project]
|
||||||
|
%td
|
||||||
|
%span= @team.human_max_project_access(project)
|
||||||
|
%td.bgred
|
||||||
|
= link_to 'Edit', edit_admin_team_project_path(@team, project), class: "btn small"
|
||||||
|
|
||||||
|
= link_to 'Relegate', admin_team_project_path(@team, project), confirm: 'Remove project from team. Are you sure?', method: :delete, class: "btn danger small", id: "relegate_project_#{project.id}"
|
||||||
|
|
||||||
|
:javascript
|
||||||
|
$(function(){
|
||||||
|
var modal = $('.change-owner-holder');
|
||||||
|
$('.change-owner-link').bind("click", function(){
|
||||||
|
$(this).hide();
|
||||||
|
modal.show();
|
||||||
|
});
|
||||||
|
$('.change-owner-cancel-link').bind("click", function(){
|
||||||
|
modal.hide();
|
||||||
|
$('.change-owner-link').show();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.groups_box
|
.ui-box
|
||||||
%h5.title
|
%h5.title
|
||||||
Groups
|
Groups
|
||||||
%small
|
%small
|
||||||
|
@ -13,8 +13,6 @@
|
||||||
%li
|
%li
|
||||||
= link_to group_path(id: group.path), class: dom_class(group) do
|
= link_to group_path(id: group.path), class: dom_class(group) do
|
||||||
%strong.well-title= truncate(group.name, length: 35)
|
%strong.well-title= truncate(group.name, length: 35)
|
||||||
%span.arrow
|
%span.right.light
|
||||||
→
|
- if group.owner == current_user
|
||||||
%span.last_activity
|
%i.icon-wrench
|
||||||
%strong Projects:
|
|
||||||
%span= current_user.authorized_projects.where(namespace_id: group.id).count
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
- if @teams.present?
|
||||||
|
= render "teams", teams: @teams
|
||||||
- if @groups.present?
|
- if @groups.present?
|
||||||
= render "groups", groups: @groups
|
= render "groups", groups: @groups
|
||||||
= render "projects", projects: @projects
|
= render "projects", projects: @projects
|
||||||
|
|
20
app/views/dashboard/_teams.html.haml
Normal file
20
app/views/dashboard/_teams.html.haml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
.ui-box
|
||||||
|
%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
|
|
@ -10,6 +10,8 @@
|
||||||
= link_to "Stats", admin_root_path
|
= link_to "Stats", admin_root_path
|
||||||
= nav_link(controller: :projects) do
|
= nav_link(controller: :projects) do
|
||||||
= link_to "Projects", admin_projects_path
|
= link_to "Projects", admin_projects_path
|
||||||
|
= nav_link(controller: :teams) do
|
||||||
|
= link_to "Teams", admin_teams_path
|
||||||
= nav_link(controller: :groups) do
|
= nav_link(controller: :groups) do
|
||||||
= link_to "Groups", admin_groups_path
|
= link_to "Groups", admin_groups_path
|
||||||
= nav_link(controller: :users) do
|
= nav_link(controller: :users) do
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
= render "layouts/head", title: "#{@group.name}"
|
= render "layouts/head", title: "#{@group.name}"
|
||||||
%body{class: "#{app_theme} application"}
|
%body{class: "#{app_theme} application"}
|
||||||
= render "layouts/flash"
|
= render "layouts/flash"
|
||||||
= render "layouts/head_panel", title: "#{@group.name}"
|
= render "layouts/head_panel", title: "group: #{@group.name}"
|
||||||
.container
|
.container
|
||||||
%ul.main_menu
|
%ul.main_menu
|
||||||
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
|
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
|
||||||
|
|
40
app/views/layouts/user_team.html.haml
Normal file
40
app/views/layouts/user_team.html.haml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
!!! 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(path: 'teams#search') do
|
||||||
|
= link_to "Search", search_team_path(@team)
|
||||||
|
|
||||||
|
= nav_link(controller: [:members]) do
|
||||||
|
= link_to team_members_path(@team), class: "team-tab tab" do
|
||||||
|
Members
|
||||||
|
|
||||||
|
- if can? current_user, :admin_user_team, @team
|
||||||
|
= nav_link(controller: [:projects]) do
|
||||||
|
= link_to team_projects_path(@team), class: "team-tab tab" do
|
||||||
|
%i.icon-briefcase
|
||||||
|
Projects
|
||||||
|
|
||||||
|
= nav_link(path: 'teams#edit') do
|
||||||
|
= link_to edit_team_path(@team), class: "stat-tab tab " do
|
||||||
|
%i.icon-edit
|
||||||
|
Edit Team
|
||||||
|
|
||||||
|
.content= yield
|
|
@ -3,7 +3,7 @@
|
||||||
= link_to project_path(@project), class: "activities-tab tab" do
|
= link_to project_path(@project), class: "activities-tab tab" do
|
||||||
%i.icon-home
|
%i.icon-home
|
||||||
Show
|
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
|
= link_to project_team_index_path(@project), class: "team-tab tab" do
|
||||||
%i.icon-user
|
%i.icon-user
|
||||||
Team
|
Team
|
||||||
|
|
22
app/views/projects/teams/available.html.haml
Normal file
22
app/views/projects/teams/available.html.haml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
= 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"
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
%h3.page_title
|
%h3.page_title
|
||||||
= "New Team member(s)"
|
= "New Team member(s)"
|
||||||
%hr
|
%hr
|
||||||
= form_for @team_member, as: :team_member, url: project_team_members_path(@project, @team_member) do |f|
|
= form_for @user_project_relation, as: :team_member, url: project_team_members_path(@project) do |f|
|
||||||
-if @team_member.errors.any?
|
-if @user_project_relation.errors.any?
|
||||||
.alert-message.block-message.error
|
.alert-message.block-message.error
|
||||||
%ul
|
%ul
|
||||||
- @team_member.errors.full_messages.each do |msg|
|
- @user_project_relation.errors.full_messages.each do |msg|
|
||||||
%li= msg
|
%li= msg
|
||||||
|
|
||||||
%h6 1. Choose people you want in the team
|
%h6 1. Choose people you want in the team
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
%h6 2. Set access level for them
|
%h6 2. Set access level for them
|
||||||
.clearfix
|
.clearfix
|
||||||
= f.label :project_access, "Project Access"
|
= 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
|
.actions
|
||||||
= f.submit 'Save', class: "btn save-btn"
|
= f.submit 'Save', class: "btn save-btn"
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
- user = member.user
|
- user = member.user
|
||||||
- allow_admin = can? current_user, :admin_project, @project
|
- 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
|
.row
|
||||||
.span6
|
.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"
|
= 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)
|
%strong= truncate(user.name, lenght: 40)
|
||||||
%br
|
%br
|
||||||
%small.cgray= user.email
|
%small.cgray= user.email
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
.span5.right
|
.span5.right
|
||||||
- if allow_admin
|
- if allow_admin
|
||||||
.left
|
.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"
|
= f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2"
|
||||||
.right
|
.right
|
||||||
- if current_user == user
|
- if current_user == user
|
||||||
|
@ -23,6 +23,6 @@
|
||||||
- elsif user.blocked
|
- elsif user.blocked
|
||||||
%span.btn.disabled.blocked Blocked
|
%span.btn.disabled.blocked Blocked
|
||||||
- elsif allow_admin
|
- 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
|
%i.icon-minus.icon-white
|
||||||
|
|
||||||
|
|
15
app/views/team_members/_show_team.html.haml
Normal file
15
app/views/team_members/_show_team.html.haml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
- 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
|
16
app/views/team_members/_teams.html.haml
Normal file
16
app/views/team_members/_teams.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
- 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();
|
||||||
|
});
|
||||||
|
})
|
|
@ -1,4 +1,4 @@
|
||||||
- if @team_member.valid?
|
- if @user_project_relation.valid?
|
||||||
:plain
|
:plain
|
||||||
$("#new_team_member").hide("slide", { direction: "right" }, 150, function(){
|
$("#new_team_member").hide("slide", { direction: "right" }, 150, function(){
|
||||||
$("#team-table").show("slide", { direction: "left" }, 150, function() {
|
$("#team-table").show("slide", { direction: "left" }, 150, function() {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
= "Import team from another project"
|
= "Import team from another project"
|
||||||
%hr
|
%hr
|
||||||
%p.slead
|
%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
|
= form_tag apply_import_project_team_members_path(@project), method: 'post' do
|
||||||
%p.slead Choose project you want to use as team source:
|
%p.slead Choose project you want to use as team source:
|
||||||
.padded
|
.padded
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
= render "projects/project_head"
|
= render "projects/project_head"
|
||||||
%h3.page_title
|
%h3.page_title
|
||||||
Team Members
|
Team Members
|
||||||
(#{@project.users_projects.count})
|
(#{@project.users.count})
|
||||||
%small
|
%small
|
||||||
Read more about project permissions
|
Read more about project permissions
|
||||||
%strong= link_to "here", help_permissions_path, class: "vlink"
|
%strong= link_to "here", help_permissions_path, class: "vlink"
|
||||||
|
@ -10,11 +10,24 @@
|
||||||
%span.right
|
%span.right
|
||||||
= link_to import_project_team_members_path(@project), class: "btn small grouped", title: "Import team from another project" do
|
= link_to import_project_team_members_path(@project), class: "btn small grouped", title: "Import team from another project" do
|
||||||
Import team from another project
|
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
|
= link_to new_project_team_member_path(@project), class: "btn success small grouped", title: "New Team Member" do
|
||||||
New Team Member
|
New Team Member
|
||||||
%hr
|
|
||||||
|
|
||||||
|
%hr
|
||||||
|
|
||||||
.clearfix
|
.clearfix
|
||||||
%div.team-table
|
%div.team-table
|
||||||
= render partial: "team_members/team", locals: {project: @project}
|
= 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}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
- allow_admin = can? current_user, :admin_project, @project
|
- allow_admin = can? current_user, :admin_project, @project
|
||||||
- user = @team_member.user
|
|
||||||
|
|
||||||
.team_member_show
|
.team_member_show
|
||||||
- if can? current_user, :admin_project, @project
|
- 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
|
.profile_avatar_holder
|
||||||
= image_tag gravatar_icon(user.email, 60), class: "borders"
|
= image_tag gravatar_icon(@member.email, 60), class: "borders"
|
||||||
%h3.page_title
|
%h3.page_title
|
||||||
= user.name
|
= @member.name
|
||||||
%small (@#{user.username})
|
%small (@#{@member.username})
|
||||||
|
|
||||||
%hr
|
%hr
|
||||||
.back_link
|
.back_link
|
||||||
|
@ -21,34 +20,34 @@
|
||||||
%table.lite
|
%table.lite
|
||||||
%tr
|
%tr
|
||||||
%td Email
|
%td Email
|
||||||
%td= mail_to user.email
|
%td= mail_to @member.email
|
||||||
%tr
|
%tr
|
||||||
%td Skype
|
%td Skype
|
||||||
%td= user.skype
|
%td= @member.skype
|
||||||
- unless user.linkedin.blank?
|
- unless @member.linkedin.blank?
|
||||||
%tr
|
%tr
|
||||||
%td LinkedIn
|
%td LinkedIn
|
||||||
%td= user.linkedin
|
%td= @member.linkedin
|
||||||
- unless user.twitter.blank?
|
- unless @member.twitter.blank?
|
||||||
%tr
|
%tr
|
||||||
%td Twitter
|
%td Twitter
|
||||||
%td= user.twitter
|
%td= @member.twitter
|
||||||
- unless user.bio.blank?
|
- unless @member.bio.blank?
|
||||||
%tr
|
%tr
|
||||||
%td Bio
|
%td Bio
|
||||||
%td= user.bio
|
%td= @member.bio
|
||||||
.span6
|
.span6
|
||||||
%table.lite
|
%table.lite
|
||||||
%tr
|
%tr
|
||||||
%td Member since
|
%td Member since
|
||||||
%td= @team_member.created_at.stamp("Aug 21, 2011")
|
%td= @user_project_relation.created_at.stamp("Aug 21, 2011")
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
Project Access:
|
Project Access:
|
||||||
%small (#{link_to "read more", help_permissions_path, class: "vlink"})
|
%small (#{link_to "read more", help_permissions_path, class: "vlink"})
|
||||||
%td
|
%td
|
||||||
= form_for(@team_member, as: :team_member, url: project_team_member_path(@project, @team_member)) do |f|
|
= 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, @team_member.project_access), {}, class: "project-access-select", disabled: !allow_admin
|
= f.select :project_access, options_for_select(Project.access_options, @user_project_relation.project_access), {}, class: "project-access-select", disabled: !allow_admin
|
||||||
%hr
|
%hr
|
||||||
= render @events
|
= render @events
|
||||||
:javascript
|
:javascript
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- if @team_member.valid?
|
- if @user_project_relation.valid?
|
||||||
:plain
|
:plain
|
||||||
$("##{dom_id(@team_member)}").effect("highlight", {color: "#529214"}, 1000);;
|
$("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#529214"}, 1000);;
|
||||||
- else
|
- else
|
||||||
:plain
|
:plain
|
||||||
$("##{dom_id(@team_member)}").effect("highlight", {color: "#D12F19"}, 1000);;
|
$("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#D12F19"}, 1000);;
|
||||||
|
|
33
app/views/teams/_filter.html.haml
Normal file
33
app/views/teams/_filter.html.haml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
= 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'
|
||||||
|
|
22
app/views/teams/_projects.html.haml
Normal file
22
app/views/teams/_projects.html.haml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
.projects_box
|
||||||
|
%h5.title
|
||||||
|
Projects
|
||||||
|
%small
|
||||||
|
(#{projects.count})
|
||||||
|
- if can? current_user, :manage_group, @group
|
||||||
|
%span.right
|
||||||
|
= link_to new_project_path(namespace_id: @group.id), class: "btn very_small info" do
|
||||||
|
%i.icon-plus
|
||||||
|
New 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
|
||||||
|
→
|
||||||
|
%span.last_activity
|
||||||
|
%strong Last activity:
|
||||||
|
%span= project_last_activity(project)
|
0
app/views/teams/_team_head.html.haml
Normal file
0
app/views/teams/_team_head.html.haml
Normal file
24
app/views/teams/edit.html.haml
Normal file
24
app/views/teams/edit.html.haml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
= render "team_head"
|
||||||
|
|
||||||
|
%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"
|
38
app/views/teams/index.html.haml
Normal file
38
app/views/teams/index.html.haml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
%h3.page_title
|
||||||
|
Teams
|
||||||
|
%small
|
||||||
|
list of all teams
|
||||||
|
|
||||||
|
= link_to 'New Team', new_team_path, class: "btn success small right"
|
||||||
|
%br
|
||||||
|
|
||||||
|
= form_tag search_teams_path, method: :get, class: 'form-inline' do
|
||||||
|
= text_field_tag :name, params[:name], class: "xlarge"
|
||||||
|
= submit_tag "Search", class: "btn submit primary"
|
||||||
|
|
||||||
|
%table.teams_list
|
||||||
|
%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, team_path(team)
|
||||||
|
%td= team.path
|
||||||
|
%td= link_to team.projects.count, team_projects_path(team)
|
||||||
|
%td= link_to team.members.count, team_members_path(team)
|
||||||
|
%td= link_to team.owner.name, team_member_path(team, team.owner)
|
||||||
|
%td.bgred
|
||||||
|
- if current_user.can?(:manage_user_team, team)
|
||||||
|
= link_to "Edit", edit_team_path(team), class: "btn small"
|
||||||
|
- if current_user.can?(:admin_user_team, team)
|
||||||
|
= link_to "Destroy", team_path(team), method: :delete, confirm: "You are shure?", class: "danger btn small"
|
||||||
|
|
25
app/views/teams/issues.html.haml
Normal file
25
app/views/teams/issues.html.haml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
= render "team_head"
|
||||||
|
|
||||||
|
%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
|
20
app/views/teams/members/_form.html.haml
Normal file
20
app/views/teams/members/_form.html.haml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
= form_tag admin_team_member_path(@team, @member), method: :put do
|
||||||
|
-if @member.errors.any?
|
||||||
|
.alert-message.block-message.error
|
||||||
|
%ul
|
||||||
|
- @member.errors.full_messages.each do |msg|
|
||||||
|
%li= msg
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
%label Default access for Team projects:
|
||||||
|
.input
|
||||||
|
= select_tag :default_project_access, options_for_select(UserTeam.access_roles, @team.default_projects_access(@member)), class: "project-access-select chosen span3"
|
||||||
|
.clearfix
|
||||||
|
%label Team admin?
|
||||||
|
.input
|
||||||
|
= check_box_tag :group_admin, true, @team.admin?(@member)
|
||||||
|
|
||||||
|
%br
|
||||||
|
.actions
|
||||||
|
= submit_tag 'Save', class: "btn primary"
|
||||||
|
= link_to 'Cancel', :back, class: "btn"
|
31
app/views/teams/members/_show.html.haml
Normal file
31
app/views/teams/members/_show.html.haml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
- 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
|
||||||
|
.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
|
16
app/views/teams/members/_team.html.haml
Normal file
16
app/views/teams/members/_team.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
- 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();
|
||||||
|
});
|
||||||
|
})
|
18
app/views/teams/members/edit.html.haml
Normal file
18
app/views/teams/members/edit.html.haml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
= render "teams/team_head"
|
||||||
|
|
||||||
|
%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'
|
19
app/views/teams/members/index.html.haml
Normal file
19
app/views/teams/members/index.html.haml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
= render "teams/team_head"
|
||||||
|
|
||||||
|
%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}
|
30
app/views/teams/members/new.html.haml
Normal file
30
app/views/teams/members/new.html.haml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
= render "teams/team_head"
|
||||||
|
|
||||||
|
%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
|
62
app/views/teams/members/show.html.haml
Normal file
62
app/views/teams/members/show.html.haml
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
= render "teams/team_head"
|
||||||
|
|
||||||
|
- 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
|
||||||
|
← 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();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
26
app/views/teams/merge_requests.html.haml
Normal file
26
app/views/teams/merge_requests.html.haml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
= render "team_head"
|
||||||
|
|
||||||
|
%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
|
19
app/views/teams/new.html.haml
Normal file
19
app/views/teams/new.html.haml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
%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"
|
||||||
|
|
||||||
|
= f.submit 'Create team', class: "btn primary"
|
||||||
|
%hr
|
||||||
|
.padded
|
||||||
|
%ul
|
||||||
|
%li All created teams are public (users can view who enter into team and which project are assigned for this team)
|
||||||
|
%li People within a team see only projects they have access to
|
||||||
|
%li You will be able to assign existing projects for team
|
16
app/views/teams/projects/_form.html.haml
Normal file
16
app/views/teams/projects/_form.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
= 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"
|
18
app/views/teams/projects/edit.html.haml
Normal file
18
app/views/teams/projects/edit.html.haml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
= render "teams/team_head"
|
||||||
|
|
||||||
|
%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'
|
34
app/views/teams/projects/index.html.haml
Normal file
34
app/views/teams/projects/index.html.haml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
= render "teams/team_head"
|
||||||
|
|
||||||
|
%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
|
||||||
|
|
||||||
|
%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"
|
25
app/views/teams/projects/new.html.haml
Normal file
25
app/views/teams/projects/new.html.haml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
= render "teams/team_head"
|
||||||
|
|
||||||
|
%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
|
11
app/views/teams/search.html.haml
Normal file
11
app/views/teams/search.html.haml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
= render "team_head"
|
||||||
|
|
||||||
|
= form_tag search_team_path(@team), method: :get, class: 'form-inline' do |f|
|
||||||
|
.padded
|
||||||
|
= label_tag :search do
|
||||||
|
%strong 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'
|
30
app/views/teams/show.html.haml
Normal file
30
app/views/teams/show.html.haml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
= render "team_head"
|
||||||
|
|
||||||
|
.projects
|
||||||
|
.activities.span8
|
||||||
|
= link_to dashboard_path, class: 'btn very_small' do
|
||||||
|
← To dashboard
|
||||||
|
|
||||||
|
%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); });
|
|
@ -21,7 +21,7 @@ Gitlab::Application.routes.draw do
|
||||||
project_root: Gitlab.config.gitolite.repos_path,
|
project_root: Gitlab.config.gitolite.repos_path,
|
||||||
upload_pack: Gitlab.config.gitolite.upload_pack,
|
upload_pack: Gitlab.config.gitolite.upload_pack,
|
||||||
receive_pack: Gitlab.config.gitolite.receive_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
|
# Help
|
||||||
|
@ -56,6 +56,7 @@ Gitlab::Application.routes.draw do
|
||||||
put :unblock
|
put :unblock
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources :groups, constraints: { id: /[^\/]+/ } do
|
resources :groups, constraints: { id: /[^\/]+/ } do
|
||||||
member do
|
member do
|
||||||
put :project_update
|
put :project_update
|
||||||
|
@ -63,18 +64,31 @@ Gitlab::Application.routes.draw do
|
||||||
delete :remove_project
|
delete :remove_project
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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 :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
|
resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, except: [:new, :create] do
|
||||||
member do
|
member do
|
||||||
get :team
|
get :team
|
||||||
put :team_update
|
put :team_update
|
||||||
end
|
end
|
||||||
|
scope module: :projects, constraints: { id: /[^\/]+/ } do
|
||||||
|
resources :members, only: [:edit, :update, :destroy]
|
||||||
end
|
end
|
||||||
resources :team_members, only: [:edit, :update, :destroy]
|
|
||||||
resources :hooks, only: [:index, :create, :destroy] do
|
|
||||||
get :test
|
|
||||||
end
|
end
|
||||||
resource :logs, only: [:show]
|
|
||||||
resource :resque, controller: 'resque', only: [:show]
|
|
||||||
root to: "dashboard#index"
|
root to: "dashboard#index"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,7 +122,6 @@ Gitlab::Application.routes.draw do
|
||||||
get "dashboard/issues" => "dashboard#issues"
|
get "dashboard/issues" => "dashboard#issues"
|
||||||
get "dashboard/merge_requests" => "dashboard#merge_requests"
|
get "dashboard/merge_requests" => "dashboard#merge_requests"
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Groups Area
|
# Groups Area
|
||||||
#
|
#
|
||||||
|
@ -122,6 +135,24 @@ Gitlab::Application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Teams Area
|
||||||
|
#
|
||||||
|
resources :teams, constraints: { id: /[^\/]+/ } do
|
||||||
|
member do
|
||||||
|
get :issues
|
||||||
|
get :merge_requests
|
||||||
|
get :search
|
||||||
|
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
|
||||||
|
collection do
|
||||||
|
get :search
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create]
|
resources :projects, constraints: { id: /[^\/]+/ }, only: [:new, :create]
|
||||||
|
|
||||||
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations }
|
devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, registrations: :registrations }
|
||||||
|
@ -238,6 +269,18 @@ Gitlab::Application.routes.draw do
|
||||||
end
|
end
|
||||||
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
|
resources :notes, only: [:index, :create, :destroy] do
|
||||||
collection do
|
collection do
|
||||||
post :preview
|
post :preview
|
||||||
|
|
11
db/migrate/20121219183753_create_user_teams.rb
Normal file
11
db/migrate/20121219183753_create_user_teams.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
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
|
|
@ -0,0 +1,11 @@
|
||||||
|
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
|
|
@ -0,0 +1,12 @@
|
||||||
|
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
|
25
db/schema.rb
25
db/schema.rb
|
@ -213,6 +213,31 @@ ActiveRecord::Schema.define(:version => 20130110172407) do
|
||||||
t.string "name"
|
t.string "name"
|
||||||
end
|
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|
|
create_table "users", :force => true do |t|
|
||||||
t.string "email", :default => "", :null => false
|
t.string "email", :default => "", :null => false
|
||||||
t.string "encrypted_password", :default => "", :null => false
|
t.string "encrypted_password", :default => "", :null => false
|
||||||
|
|
70
features/admin/teams.feature
Normal file
70
features/admin/teams.feature
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
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
|
234
features/steps/admin/admin_teams.rb
Normal file
234
features/steps/admin/admin_teams.rb
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
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
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue