Merge branch 'feature/users_groups' into 6-0-dev
This commit is contained in:
commit
30d2d2381f
41 changed files with 406 additions and 262 deletions
|
@ -32,6 +32,8 @@ class Dispatcher
|
|||
new Wall(project_id)
|
||||
when 'teams:members:index'
|
||||
new TeamMembers()
|
||||
when 'groups:people'
|
||||
new GroupMembers()
|
||||
|
||||
switch path.first()
|
||||
when 'admin' then new Admin()
|
||||
|
|
6
app/assets/javascripts/groups.js.coffee
Normal file
6
app/assets/javascripts/groups.js.coffee
Normal file
|
@ -0,0 +1,6 @@
|
|||
class GroupMembers
|
||||
constructor: ->
|
||||
$('li.users_group').bind 'ajax:success', ->
|
||||
$(this).fadeOut()
|
||||
|
||||
@GroupMembers = GroupMembers
|
|
@ -16,6 +16,12 @@
|
|||
color: #888;
|
||||
}
|
||||
|
||||
&.unstyled {
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.smoke { background-color: #f5f5f5; }
|
||||
|
||||
&:hover {
|
||||
|
|
|
@ -66,14 +66,12 @@ class Admin::GroupsController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def project_teams_update
|
||||
@group.add_users_to_project_teams(params[:user_ids].split(','), params[:project_access])
|
||||
@group.add_users(params[:user_ids].split(','), params[:group_access])
|
||||
|
||||
redirect_to [:admin, @group], notice: 'Users were successfully added.'
|
||||
end
|
||||
|
||||
def destroy
|
||||
@group.truncate_teams
|
||||
|
||||
@group.destroy
|
||||
|
||||
redirect_to admin_groups_path, notice: 'Group was successfully deleted.'
|
||||
|
|
|
@ -34,11 +34,12 @@ class DashboardController < ApplicationController
|
|||
@projects
|
||||
end
|
||||
|
||||
@projects = @projects.tagged_with(params[:label]) if params[:label].present?
|
||||
@projects = @projects.search(params[:search]) if params[:search].present?
|
||||
@projects = @projects.page(params[:page]).per(30)
|
||||
|
||||
@labels = Project.where(id: @projects.map(&:id)).tags_on(:labels)
|
||||
|
||||
@projects = @projects.tagged_with(params[:label]) if params[:label].present?
|
||||
@projects = @projects.page(params[:page]).per(30)
|
||||
end
|
||||
|
||||
# Get authored or assigned open merge requests
|
||||
|
|
|
@ -10,7 +10,7 @@ class GraphsController < ProjectResourceController
|
|||
format.js do
|
||||
@repo = @project.repository
|
||||
@stats = Gitlab::Git::GitStats.new(@repo.raw, @repo.root_ref)
|
||||
@log = @stats.parsed_log.to_json
|
||||
@log = @stats.parsed_log.to_json rescue []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class GroupsController < ApplicationController
|
||||
respond_to :html
|
||||
before_filter :group, except: [:new, :create]
|
||||
before_filter :group, except: [:new, :create, :people]
|
||||
|
||||
# Authorize
|
||||
before_filter :authorize_read_group!, except: [:new, :create]
|
||||
|
@ -63,19 +63,8 @@ class GroupsController < ApplicationController
|
|||
|
||||
def people
|
||||
@project = group.projects.find(params[:project_id]) if params[:project_id]
|
||||
@users = @project ? @project.users : group.users
|
||||
@users.sort_by!(&:name)
|
||||
|
||||
if @project
|
||||
@team_member = @project.users_projects.new
|
||||
else
|
||||
@team_member = UsersProject.new
|
||||
end
|
||||
end
|
||||
|
||||
def team_members
|
||||
@group.add_users_to_project_teams(params[:user_ids].split(','), params[:project_access])
|
||||
redirect_to people_group_path(@group), notice: 'Users were successfully added.'
|
||||
@members = group.users_groups.order('group_access DESC')
|
||||
@users_group = UsersGroup.new
|
||||
end
|
||||
|
||||
def edit
|
||||
|
@ -83,7 +72,7 @@ class GroupsController < ApplicationController
|
|||
|
||||
def update
|
||||
group_params = params[:group].dup
|
||||
owner_id =group_params.delete(:owner_id)
|
||||
owner_id = group_params.delete(:owner_id)
|
||||
|
||||
if owner_id
|
||||
@group.owner = User.find(owner_id)
|
||||
|
|
|
@ -23,7 +23,7 @@ class IssuesController < ProjectResourceController
|
|||
|
||||
assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
|
||||
|
||||
@assignee = @project.users.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
|
||||
@assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
|
||||
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
|
||||
|
||||
respond_to do |format|
|
||||
|
|
|
@ -4,10 +4,8 @@ class TeamMembersController < ProjectResourceController
|
|||
before_filter :authorize_admin_project!, except: [:index, :show]
|
||||
|
||||
def index
|
||||
@team = @project.users_projects.scoped
|
||||
@team = @team.send(params[:type]) if %w(masters developers reporters guests).include?(params[:type])
|
||||
@team = @team.sort_by(&:project_access).reverse.group_by(&:project_access)
|
||||
|
||||
@group = @project.group
|
||||
@users_projects = @project.users_projects.order('project_access DESC')
|
||||
@assigned_teams = @project.user_team_project_relationships
|
||||
end
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ class UsersController < ApplicationController
|
|||
|
||||
def show
|
||||
@user = User.find_by_username!(params[:username])
|
||||
@projects = @user.authorized_projects.where('projects.id in (?)', current_user.authorized_projects.map(&:id))
|
||||
@projects = @user.authorized_projects.where('projects.id in (?)', current_user.authorized_projects.map(&:id)).order('namespace_id DESC')
|
||||
@events = @user.recent_events.where(project_id: @projects.map(&:id)).limit(20)
|
||||
|
||||
@title = @user.name
|
||||
|
|
40
app/controllers/users_groups_controller.rb
Normal file
40
app/controllers/users_groups_controller.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
class UsersGroupsController < ApplicationController
|
||||
before_filter :group
|
||||
|
||||
# Authorize
|
||||
before_filter :authorize_admin_group!
|
||||
|
||||
layout 'group'
|
||||
|
||||
def create
|
||||
@group.add_users(params[:user_ids].split(','), params[:group_access])
|
||||
|
||||
redirect_to people_group_path(@group), notice: 'Users were successfully added.'
|
||||
end
|
||||
|
||||
def update
|
||||
# TODO: implement
|
||||
end
|
||||
|
||||
def destroy
|
||||
@users_group = @group.users_groups.find(params[:id])
|
||||
@users_group.destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to people_group_path(@group), notice: 'User was successfully removed from group.' }
|
||||
format.js { render nothing: true }
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def group
|
||||
@group ||= Group.find_by_path(params[:group_id])
|
||||
end
|
||||
|
||||
def authorize_admin_group!
|
||||
unless can?(current_user, :manage_group, group)
|
||||
return render_404
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,4 +14,8 @@ module GroupsHelper
|
|||
merge_requests_group_path(@group, options)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_user_from_group_message(group, user)
|
||||
"You are going to remove #{user.name} from #{group.name} Group. Are you sure?"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,6 +50,10 @@ class Ability
|
|||
rules << project_admin_rules
|
||||
end
|
||||
|
||||
if project.group && project.group.owners.include?(user)
|
||||
rules << project_admin_rules
|
||||
end
|
||||
|
||||
rules.flatten
|
||||
end
|
||||
|
||||
|
@ -132,7 +136,7 @@ class Ability
|
|||
rules = []
|
||||
|
||||
# Only group owner and administrators can manage group
|
||||
if group.owner == user || user.admin?
|
||||
if group.owners.include?(user) || user.admin?
|
||||
rules << [
|
||||
:manage_group,
|
||||
:manage_namespace
|
||||
|
|
|
@ -13,26 +13,28 @@
|
|||
#
|
||||
|
||||
class Group < Namespace
|
||||
has_many :users_groups, dependent: :destroy
|
||||
has_many :users, through: :users_groups
|
||||
|
||||
def add_users_to_project_teams(user_ids, project_access)
|
||||
UsersProject.add_users_into_projects(
|
||||
projects.map(&:id),
|
||||
user_ids,
|
||||
project_access
|
||||
)
|
||||
end
|
||||
|
||||
def users
|
||||
users = User.joins(:users_projects).where(users_projects: {project_id: project_ids})
|
||||
users = users << owner
|
||||
users.uniq
|
||||
end
|
||||
after_create :add_owner
|
||||
|
||||
def human_name
|
||||
name
|
||||
end
|
||||
|
||||
def truncate_teams
|
||||
UsersProject.truncate_teams(project_ids)
|
||||
def owners
|
||||
@owners ||= (users_groups.owners.map(&:user) << owner)
|
||||
end
|
||||
|
||||
def add_users(user_ids, group_access)
|
||||
user_ids.compact.each do |user_id|
|
||||
self.users_groups.create(user_id: user_id, group_access: group_access)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_owner
|
||||
self.add_users([owner.id], UsersGroup::OWNER)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,6 +21,16 @@ class ProjectTeam
|
|||
end
|
||||
end
|
||||
|
||||
def find user_id
|
||||
user = project.users.find_by_id(user_id)
|
||||
|
||||
if group
|
||||
user ||= group.users.find_by_id(user_id)
|
||||
end
|
||||
|
||||
user
|
||||
end
|
||||
|
||||
def get_tm user_id
|
||||
project.users_projects.find_by_user_id(user_id)
|
||||
end
|
||||
|
@ -47,23 +57,23 @@ class ProjectTeam
|
|||
end
|
||||
|
||||
def members
|
||||
project.users_projects
|
||||
fetch_members
|
||||
end
|
||||
|
||||
def guests
|
||||
members.guests.map(&:user)
|
||||
@guests ||= fetch_members(:guests)
|
||||
end
|
||||
|
||||
def reporters
|
||||
members.reporters.map(&:user)
|
||||
@reporters ||= fetch_members(:reporters)
|
||||
end
|
||||
|
||||
def developers
|
||||
members.developers.map(&:user)
|
||||
@developers ||= fetch_members(:developers)
|
||||
end
|
||||
|
||||
def masters
|
||||
members.masters.map(&:user)
|
||||
@masters ||= fetch_members(:masters)
|
||||
end
|
||||
|
||||
def import(source_project)
|
||||
|
@ -96,4 +106,22 @@ class ProjectTeam
|
|||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_members(level = nil)
|
||||
project_members = project.users_projects
|
||||
group_members = group ? group.users_groups : []
|
||||
|
||||
if level
|
||||
project_members = project_members.send(level)
|
||||
group_members = group_members.send(level) if group
|
||||
end
|
||||
|
||||
(project_members + group_members).map(&:user).uniq
|
||||
end
|
||||
|
||||
def group
|
||||
project.group
|
||||
end
|
||||
end
|
||||
|
|
|
@ -71,7 +71,9 @@ class User < ActiveRecord::Base
|
|||
has_many :keys, dependent: :destroy
|
||||
|
||||
# Groups
|
||||
has_many :groups, class_name: "Group", foreign_key: :owner_id
|
||||
has_many :own_groups, class_name: "Group", foreign_key: :owner_id
|
||||
has_many :users_groups, dependent: :destroy
|
||||
has_many :groups, through: :users_groups
|
||||
|
||||
# Teams
|
||||
has_many :own_teams, dependent: :destroy, class_name: "UserTeam", foreign_key: :owner_id
|
||||
|
@ -230,7 +232,7 @@ class User < ActiveRecord::Base
|
|||
|
||||
# Groups where user is an owner
|
||||
def owned_groups
|
||||
groups
|
||||
own_groups
|
||||
end
|
||||
|
||||
def owned_teams
|
||||
|
@ -239,14 +241,14 @@ class User < ActiveRecord::Base
|
|||
|
||||
# Groups user has access to
|
||||
def authorized_groups
|
||||
@group_ids ||= (groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
|
||||
@group_ids ||= (groups.pluck(:id) + own_groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
|
||||
Group.where(id: @group_ids)
|
||||
end
|
||||
|
||||
|
||||
# Projects user has access to
|
||||
def authorized_projects
|
||||
@project_ids ||= (owned_projects.pluck(:id) + projects.pluck(:id)).uniq
|
||||
@project_ids ||= (owned_projects.pluck(:id) + groups.map(&:projects).flatten.map(&:id) + projects.pluck(:id)).uniq
|
||||
Project.where(id: @project_ids)
|
||||
end
|
||||
|
||||
|
|
42
app/models/users_group.rb
Normal file
42
app/models/users_group.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
class UsersGroup < ActiveRecord::Base
|
||||
GUEST = 10
|
||||
REPORTER = 20
|
||||
DEVELOPER = 30
|
||||
MASTER = 40
|
||||
OWNER = 50
|
||||
|
||||
def self.group_access_roles
|
||||
{
|
||||
"Guest" => GUEST,
|
||||
"Reporter" => REPORTER,
|
||||
"Developer" => DEVELOPER,
|
||||
"Master" => MASTER,
|
||||
"Owner" => OWNER
|
||||
}
|
||||
end
|
||||
|
||||
attr_accessible :group_access, :user_id
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :group
|
||||
|
||||
scope :guests, -> { where(group_access: GUEST) }
|
||||
scope :reporters, -> { where(group_access: REPORTER) }
|
||||
scope :developers, -> { where(group_access: DEVELOPER) }
|
||||
scope :masters, -> { where(group_access: MASTER) }
|
||||
scope :owners, -> { where(group_access: OWNER) }
|
||||
|
||||
scope :with_group, ->(group) { where(group_id: group.id) }
|
||||
scope :with_user, ->(user) { where(user_id: user.id) }
|
||||
|
||||
validates :group_access, inclusion: { in: UsersGroup.group_access_roles.values }, presence: true
|
||||
validates :user_id, presence: true
|
||||
validates :group_id, presence: true
|
||||
validates :user_id, uniqueness: { scope: [:group_id], message: "already exists in group" }
|
||||
|
||||
delegate :name, :username, :email, to: :user, prefix: true
|
||||
|
||||
def human_access
|
||||
UsersGroup.group_access_roles.key(self.group_access)
|
||||
end
|
||||
end
|
|
@ -129,9 +129,7 @@ class UsersProject < ActiveRecord::Base
|
|||
Project.access_options.key(self.project_access)
|
||||
end
|
||||
|
||||
def repo_access_human
|
||||
self.class.access_roles.invert[self.project_access]
|
||||
end
|
||||
alias_method :human_access, :project_access_human
|
||||
|
||||
def skip_git?
|
||||
!!@skip_git
|
||||
|
|
|
@ -43,7 +43,7 @@ class SystemHooksService
|
|||
project_id: model.project_id,
|
||||
user_name: model.user.name,
|
||||
user_email: model.user.email,
|
||||
project_access: model.repo_access_human
|
||||
project_access: model.project_access_human
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -49,36 +49,6 @@
|
|||
%strong
|
||||
= @group.created_at.stamp("March 1, 1999")
|
||||
|
||||
|
||||
.ui-box
|
||||
%h5.title
|
||||
Add user to Group projects:
|
||||
.ui-box-body.form-holder
|
||||
%p.light
|
||||
Read more about project permissions
|
||||
%strong= link_to "here", help_permissions_path, class: "vlink"
|
||||
|
||||
= form_tag project_teams_update_admin_group_path(@group), id: "new_team_member", class: "bulk_import", method: :put do
|
||||
%div
|
||||
= users_select_tag(:user_ids, multiple: true)
|
||||
%div.prepend-top-10
|
||||
= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span2"}
|
||||
%hr
|
||||
= submit_tag 'Add user to projects in group', class: "btn btn-create"
|
||||
.ui-box
|
||||
%h5.title
|
||||
Users from Group projects
|
||||
%small
|
||||
(#{@group.users.count})
|
||||
%ul.well-list
|
||||
- @group.users.sort_by(&:name).each do |user|
|
||||
%li{class: dom_class(user)}
|
||||
%strong
|
||||
= link_to user.name, admin_user_path(user)
|
||||
%span.pull-right.light
|
||||
= pluralize user.authorized_projects.in_namespace(@group).count, 'project'
|
||||
|
||||
.span6
|
||||
.ui-box
|
||||
%h5.title
|
||||
Projects
|
||||
|
@ -91,3 +61,34 @@
|
|||
= link_to project.name_with_namespace, [:admin, project]
|
||||
%span.pull-right.light
|
||||
%span.monospace= project.path_with_namespace + ".git"
|
||||
|
||||
.span6
|
||||
.ui-box
|
||||
%h5.title
|
||||
Add user(s):
|
||||
.ui-box-body.form-holder
|
||||
%p.light
|
||||
Read more about project permissions
|
||||
%strong= link_to "here", help_permissions_path, class: "vlink"
|
||||
|
||||
= form_tag project_teams_update_admin_group_path(@group), id: "new_team_member", class: "bulk_import", method: :put do
|
||||
%div
|
||||
= users_select_tag(:user_ids, multiple: true)
|
||||
%div.prepend-top-10
|
||||
= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span2"}
|
||||
%hr
|
||||
= submit_tag 'Add users into group', class: "btn btn-create"
|
||||
.ui-box
|
||||
%h5.title
|
||||
Users from #{@group.name} Group
|
||||
%small
|
||||
(#{@group.users_groups.count})
|
||||
%ul.well-list
|
||||
- @group.users_groups.order('group_access DESC').each do |member|
|
||||
- user = member.user
|
||||
%li{class: dom_class(user)}
|
||||
%strong
|
||||
= link_to user.name, admin_user_path(user)
|
||||
%span.pull-right.light
|
||||
= member.human_access
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
= form_for @team_member, as: :team_member, url: team_members_group_path(@group) do |f|
|
||||
= form_for @users_group, url: group_users_groups_path(@group) do |f|
|
||||
%fieldset
|
||||
%legend= "New Team member(s) for projects in #{@group.name}"
|
||||
%legend= "New Group member(s) for #{@group.name}"
|
||||
|
||||
%h6 1. Choose people you want in the team
|
||||
%h6 1. Choose people you want in the group
|
||||
.clearfix
|
||||
= f.label :user_ids, "People"
|
||||
.input= users_select_tag(:user_ids, multiple: true)
|
||||
.input= users_select_tag(:user_ids, multiple: true, class: 'input-large')
|
||||
|
||||
%h6 2. Set access level for them
|
||||
.clearfix
|
||||
= f.label :project_access, "Project Access"
|
||||
.input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select chosen"
|
||||
= f.label :group_access, "Group Access"
|
||||
.input= select_tag :group_access, options_for_select(UsersGroup.group_access_roles, @users_group.group_access), class: "project-access-select chosen"
|
||||
|
||||
.form-actions
|
||||
= hidden_field_tag :redirect_to, people_group_path(@group)
|
||||
= f.submit 'Add', class: "btn btn-save"
|
||||
= f.submit 'Add users into group', class: "btn btn-create"
|
||||
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
= form_for @team_member, as: :team_member, url: project_team_members_path(@project, @team_member) do |f|
|
||||
%fieldset
|
||||
%legend= "New Team member(s) for #{@project.name}"
|
||||
|
||||
%h6 1. Choose people you want in the team
|
||||
.clearfix
|
||||
= f.label :user_ids, "People"
|
||||
.input= users_select_tag(:user_ids, multiple: true)
|
||||
|
||||
%h6 2. Set access level for them
|
||||
.clearfix
|
||||
= f.label :project_access, "Project Access"
|
||||
.input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select chosen"
|
||||
|
||||
.form-actions
|
||||
= hidden_field_tag :redirect_to, people_group_path(@group, project_id: @project.id)
|
||||
= f.submit 'Add', class: "btn btn-save"
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
= form_tag people_group_path(@group), method: 'get' do
|
||||
%fieldset
|
||||
%legend Projects:
|
||||
%ul.nav.nav-pills.nav-stacked
|
||||
- @projects.each do |project|
|
||||
%li{class: ("active" if params[:project_id] == project.id.to_s)}
|
||||
= link_to people_group_path(@group, project_id: project.id) do
|
||||
= project.name_with_namespace
|
||||
%small.pull-right= project.users.count
|
||||
- if @projects.blank?
|
||||
%p.nothing_here_message This group has no projects yet
|
||||
|
||||
%fieldset
|
||||
%hr
|
||||
= link_to "Reset", people_group_path(@group), class: 'btn pull-right'
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
- can_manage_group = current_user.can? :manage_group, @group
|
||||
.row
|
||||
.span3
|
||||
= render 'people_filter'
|
||||
.span9
|
||||
- if can?(current_user, :manage_group, @group)
|
||||
= render (@project ? "new_member" : "new_group_member")
|
||||
.span6
|
||||
- if can_manage_group
|
||||
= render "new_group_member"
|
||||
- else
|
||||
.light-well
|
||||
%h4.nothing_here_message
|
||||
Only group owners can manage group members
|
||||
.span6
|
||||
.ui-box
|
||||
%h5.title
|
||||
Team
|
||||
#{@group.name} Group Members
|
||||
%small
|
||||
(#{@users.size})
|
||||
(#{@members.count})
|
||||
%ul.well-list
|
||||
- @users.each do |user|
|
||||
%li
|
||||
= image_tag gravatar_icon(user.email, 16), class: "avatar s16"
|
||||
%strong= user.name
|
||||
%span.cgray= user.email
|
||||
- if @group.owner == user
|
||||
%span.btn.btn-small.disabled.pull-right Group Owner
|
||||
|
||||
- @members.each do |member|
|
||||
= render 'users_groups/users_group', member: member, show_controls: can_manage_group
|
||||
%p.light
|
||||
Group members get access to all projects in this group
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
Assign to
|
||||
.input
|
||||
.pull-left
|
||||
= f.select(:assignee_id, @project.users.alphabetically.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'})
|
||||
= f.select(:assignee_id, @project.team.members.sort_by(&:name).map {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'})
|
||||
.pull-right
|
||||
|
||||
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
%span.update_issues_text Update selected issues with
|
||||
.left
|
||||
= select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status")
|
||||
= select_tag('update[assignee_id]', options_from_collection_for_select(@project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee")
|
||||
= select_tag('update[assignee_id]', options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id]), prompt: "Assignee")
|
||||
= select_tag('update[milestone_id]', options_from_collection_for_select(issues_active_milestones, "id", "title", params[:milestone_id]), prompt: "Milestone")
|
||||
= hidden_field_tag 'update[issues_ids]', []
|
||||
= hidden_field_tag :status, params[:status]
|
||||
|
@ -50,7 +50,7 @@
|
|||
Any
|
||||
= link_to project_issues_with_filter_path(@project, assignee_id: 0) do
|
||||
Unassigned
|
||||
- @project.users.sort_by(&:name).each do |user|
|
||||
- @project.team.members.sort_by(&:name).each do |user|
|
||||
%li
|
||||
= link_to project_issues_with_filter_path(@project, assignee_id: user.id) do
|
||||
= image_tag gravatar_icon(user.email), class: "avatar s16"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
= nav_link(controller: [:team_members, :teams]) do
|
||||
= link_to project_team_index_path(@project), class: "team-tab tab" do
|
||||
%i.icon-group
|
||||
Project Members
|
||||
Members
|
||||
= nav_link(controller: :deploy_keys) do
|
||||
= link_to project_deploy_keys_path(@project) do
|
||||
%span
|
||||
|
|
10
app/views/team_members/_group_members.html.haml
Normal file
10
app/views/team_members/_group_members.html.haml
Normal file
|
@ -0,0 +1,10 @@
|
|||
.ui-box
|
||||
%h5.title
|
||||
%strong #{@group.name} Group
|
||||
members (#{@group.users_groups.count})
|
||||
.pull-right
|
||||
= link_to people_group_path(@group), class: 'btn btn-small' do
|
||||
%i.icon-edit
|
||||
%ul.well-list
|
||||
- @group.users_groups.order('group_access DESC').each do |member|
|
||||
= render 'users_groups/users_group', member: member, show_controls: false
|
|
@ -1,10 +1,9 @@
|
|||
- can_admin_project = (can? current_user, :admin_project, @project)
|
||||
- team.each do |access, members|
|
||||
- role = Project.access_options.key(access).pluralize
|
||||
.ui-box{class: role.downcase}
|
||||
.team-table
|
||||
- can_admin_project = (can? current_user, :admin_project, @project)
|
||||
.ui-box
|
||||
%h5.title
|
||||
= role
|
||||
%span.light (#{members.size})
|
||||
%strong #{@project.name} Project
|
||||
members (#{members.count})
|
||||
%ul.well-list
|
||||
- members.sort_by(&:user_name).each do |team_member|
|
||||
- members.each do |team_member|
|
||||
= render 'team_members/team_member', member: team_member, current_user_can_admin_project: can_admin_project
|
||||
|
|
|
@ -1,27 +1,17 @@
|
|||
- user = member.user
|
||||
- allow_admin = current_user_can_admin_project
|
||||
%li{id: dom_id(user), class: "team_member_row user_#{user.id}"}
|
||||
.row
|
||||
.span4
|
||||
= link_to user, title: user.name, class: "dark" do
|
||||
= image_tag gravatar_icon(user.email, 32), class: "avatar s32"
|
||||
%strong= truncate(user.name, lenght: 40)
|
||||
%br
|
||||
%small.cgray= user.username
|
||||
|
||||
.span4.pull-right
|
||||
.pull-right
|
||||
- if allow_admin
|
||||
.left
|
||||
.pull-left
|
||||
= 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 trigger-submit"
|
||||
.pull-right
|
||||
- if current_user == user
|
||||
%span.label.label-success This is you!
|
||||
- if @project.namespace_owner == user
|
||||
%span.label.label-info Owner
|
||||
- elsif user.blocked?
|
||||
%span.label.label-error Blocked
|
||||
- elsif allow_admin
|
||||
|
||||
= link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from team' do
|
||||
%i.icon-minus.icon-white
|
||||
= image_tag gravatar_icon(user.email, 32), class: "avatar s32"
|
||||
%p
|
||||
%strong= user.name
|
||||
%span.cgray= user.username
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
= render "projects/settings_nav"
|
||||
%h3.page_title
|
||||
Project Members
|
||||
(#{@project.users.count})
|
||||
%small
|
||||
Read more about project permissions
|
||||
%strong= link_to "here", help_permissions_path, class: "vlink"
|
||||
Users with access to this project
|
||||
|
||||
- if can? current_user, :admin_team_member, @project
|
||||
%span.pull-right
|
||||
|
@ -15,42 +11,25 @@
|
|||
= link_to new_project_team_member_path(@project), class: "btn btn-primary small grouped", title: "New Team Member" do
|
||||
New Team Member
|
||||
|
||||
%hr
|
||||
%p.light
|
||||
Read more about project permissions
|
||||
%strong= link_to "here", help_permissions_path, class: "vlink"
|
||||
|
||||
.clearfix
|
||||
.row
|
||||
.span3
|
||||
%ul.nav.nav-pills.nav-stacked
|
||||
%li{class: ("active" if !params[:type])}
|
||||
= link_to project_team_index_path(type: nil) do
|
||||
All
|
||||
%li{class: ("active" if params[:type] == 'masters')}
|
||||
= link_to project_team_index_path(type: 'masters') do
|
||||
Masters
|
||||
%span.pull-right= @project.users_projects.masters.count
|
||||
%li{class: ("active" if params[:type] == 'developers')}
|
||||
= link_to project_team_index_path(type: 'developers') do
|
||||
Developers
|
||||
%span.pull-right= @project.users_projects.developers.count
|
||||
%li{class: ("active" if params[:type] == 'reporters')}
|
||||
= link_to project_team_index_path(type: 'reporters') do
|
||||
Reporters
|
||||
%span.pull-right= @project.users_projects.reporters.count
|
||||
%li{class: ("active" if params[:type] == 'guests')}
|
||||
= link_to project_team_index_path(type: 'guests') do
|
||||
Guests
|
||||
%span.pull-right= @project.users_projects.guests.count
|
||||
|
||||
- if @assigned_teams.present?
|
||||
- if @group
|
||||
.row
|
||||
.span6
|
||||
= render "team_members/group_members"
|
||||
.span6
|
||||
= render "team_members/team", members: @users_projects
|
||||
|
||||
- else
|
||||
= render "team_members/team", members: @users_projects
|
||||
|
||||
- if @assigned_teams.present?
|
||||
%h5
|
||||
Assigned teams
|
||||
(#{@project.user_teams.count})
|
||||
%div
|
||||
= render "team_members/assigned_teams", assigned_teams: @assigned_teams
|
||||
|
||||
.span9
|
||||
%div.team-table
|
||||
= render "team_members/team", team: @team
|
||||
|
||||
|
||||
|
||||
|
|
16
app/views/users_groups/_users_group.html.haml
Normal file
16
app/views/users_groups/_users_group.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
|||
- user = member.user
|
||||
- return unless user
|
||||
%li{class: dom_class(member)}
|
||||
= image_tag gravatar_icon(user.email, 16), class: "avatar s16"
|
||||
%strong= user.name
|
||||
%span.cgray= user.username
|
||||
|
||||
%span.pull-right
|
||||
- if @group.owners.include?(user)
|
||||
%span.label.label-info Group Owner
|
||||
- else
|
||||
= member.human_access
|
||||
|
||||
- if show_controls && user != current_user
|
||||
= link_to group_users_group_path(@group, member), confirm: remove_user_from_group_message(@group, user), method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
|
||||
%i.icon-minus.icon-white
|
|
@ -149,10 +149,10 @@ Gitlab::Application.routes.draw do
|
|||
member do
|
||||
get :issues
|
||||
get :merge_requests
|
||||
get :search
|
||||
get :people
|
||||
post :team_members
|
||||
end
|
||||
|
||||
resources :users_groups, only: [:create, :update, :destroy]
|
||||
end
|
||||
|
||||
#
|
||||
|
|
11
db/migrate/20130617095603_create_users_groups.rb
Normal file
11
db/migrate/20130617095603_create_users_groups.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
class CreateUsersGroups < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :users_groups do |t|
|
||||
t.integer :group_access, null: false
|
||||
t.integer :group_id, null: false
|
||||
t.integer :user_id, null: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
52
db/schema.rb
52
db/schema.rb
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20130614132337) do
|
||||
ActiveRecord::Schema.define(:version => 20130617095603) do
|
||||
|
||||
create_table "deploy_keys_projects", :force => true do |t|
|
||||
t.integer "deploy_key_id", :null => false
|
||||
|
@ -53,8 +53,8 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
t.integer "assignee_id"
|
||||
t.integer "author_id"
|
||||
t.integer "project_id"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "position", :default => 0
|
||||
t.string "branch_name"
|
||||
t.text "description"
|
||||
|
@ -71,8 +71,8 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
|
||||
create_table "keys", :force => true do |t|
|
||||
t.integer "user_id"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.text "key"
|
||||
t.string "title"
|
||||
t.string "identifier"
|
||||
|
@ -89,8 +89,8 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
t.integer "author_id"
|
||||
t.integer "assignee_id"
|
||||
t.string "title"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.text "st_commits", :limit => 2147483647
|
||||
t.text "st_diffs", :limit => 2147483647
|
||||
t.integer "milestone_id"
|
||||
|
@ -139,8 +139,8 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
t.text "note"
|
||||
t.string "noteable_type"
|
||||
t.integer "author_id"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "project_id"
|
||||
t.string "attachment"
|
||||
t.string "line_code"
|
||||
|
@ -158,8 +158,8 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
t.string "name"
|
||||
t.string "path"
|
||||
t.text "description"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "creator_id"
|
||||
t.string "default_branch"
|
||||
t.boolean "issues_enabled", :default => true, :null => false
|
||||
|
@ -206,8 +206,8 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
t.text "content"
|
||||
t.integer "author_id", :null => false
|
||||
t.integer "project_id"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "file_name"
|
||||
t.datetime "expires_at"
|
||||
t.boolean "private", :default => true, :null => false
|
||||
|
@ -228,9 +228,6 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
t.datetime "created_at"
|
||||
end
|
||||
|
||||
add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
|
||||
add_index "taggings", ["taggable_id", "taggable_type", "context"], :name => "index_taggings_on_taggable_id_and_taggable_type_and_context"
|
||||
|
||||
create_table "tags", :force => true do |t|
|
||||
t.string "name"
|
||||
end
|
||||
|
@ -263,7 +260,7 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
|
||||
create_table "users", :force => true do |t|
|
||||
t.string "email", :default => "", :null => false
|
||||
t.string "encrypted_password", :default => "", :null => false
|
||||
t.string "encrypted_password", :limit => 128, :default => "", :null => false
|
||||
t.string "reset_password_token"
|
||||
t.datetime "reset_password_sent_at"
|
||||
t.datetime "remember_created_at"
|
||||
|
@ -272,8 +269,8 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
t.datetime "last_sign_in_at"
|
||||
t.string "current_sign_in_ip"
|
||||
t.string "last_sign_in_ip"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "name"
|
||||
t.boolean "admin", :default => false, :null => false
|
||||
t.integer "projects_limit", :default => 10
|
||||
|
@ -299,16 +296,23 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
|
||||
add_index "users", ["admin"], :name => "index_users_on_admin"
|
||||
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
|
||||
add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true
|
||||
add_index "users", ["name"], :name => "index_users_on_name"
|
||||
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
|
||||
add_index "users", ["username"], :name => "index_users_on_username"
|
||||
|
||||
create_table "users_groups", :force => true do |t|
|
||||
t.integer "group_access", :null => false
|
||||
t.integer "group_id", :null => false
|
||||
t.integer "user_id", :null => false
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
end
|
||||
|
||||
create_table "users_projects", :force => true do |t|
|
||||
t.integer "user_id", :null => false
|
||||
t.integer "project_id", :null => false
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "project_access", :default => 0, :null => false
|
||||
t.integer "notification_level", :default => 3, :null => false
|
||||
end
|
||||
|
@ -320,8 +324,8 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
|
|||
create_table "web_hooks", :force => true do |t|
|
||||
t.string "url"
|
||||
t.integer "project_id"
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "type", :default => "ProjectHook"
|
||||
t.integer "service_id"
|
||||
end
|
||||
|
|
|
@ -45,7 +45,7 @@ class AdminGroups < Spinach::FeatureSteps
|
|||
within "#new_team_member" do
|
||||
select "Reporter", from: "project_access"
|
||||
end
|
||||
click_button "Add user to projects in group"
|
||||
click_button "Add users into group"
|
||||
end
|
||||
|
||||
Then 'I should see "John" in team list in every project as "Reporter"' do
|
||||
|
|
|
@ -39,11 +39,11 @@ class Groups < Spinach::FeatureSteps
|
|||
|
||||
And 'I select user "John" from list with role "Reporter"' do
|
||||
user = User.find_by_name("John")
|
||||
within "#new_team_member" do
|
||||
within ".new_users_group" do
|
||||
select2(user.id, from: "#user_ids", multiple: true)
|
||||
select "Reporter", from: "project_access"
|
||||
select "Reporter", from: "group_access"
|
||||
end
|
||||
click_button "Add"
|
||||
click_button "Add users into group"
|
||||
end
|
||||
|
||||
Then 'I should see user "John" in team list' do
|
||||
|
|
|
@ -45,7 +45,7 @@ class ProjectActiveTab < Spinach::FeatureSteps
|
|||
# Sub Tabs: Home
|
||||
|
||||
Given 'I click the "Team" tab' do
|
||||
click_link('Project Members')
|
||||
click_link('Members')
|
||||
end
|
||||
|
||||
Given 'I click the "Attachments" tab' do
|
||||
|
@ -73,7 +73,7 @@ class ProjectActiveTab < Spinach::FeatureSteps
|
|||
end
|
||||
|
||||
Then 'the active sub tab should be Team' do
|
||||
ensure_active_sub_tab('Project Members')
|
||||
ensure_active_sub_tab('Members')
|
||||
end
|
||||
|
||||
Then 'the active sub tab should be Attachments' do
|
||||
|
|
|
@ -30,14 +30,20 @@ class ProjectTeamManagement < Spinach::FeatureSteps
|
|||
end
|
||||
|
||||
Then 'I should see "Mike" in team list as "Reporter"' do
|
||||
within '.reporters' do
|
||||
user = User.find_by_name("Mike")
|
||||
|
||||
within "#user_#{user.id}" do
|
||||
page.should have_content('Mike')
|
||||
page.find('#team_member_project_access').value.should == access_value(:reporter)
|
||||
end
|
||||
end
|
||||
|
||||
Given 'I should see "Sam" in team list as "Developer"' do
|
||||
within '.developers' do
|
||||
user = User.find_by_name("Sam")
|
||||
|
||||
within "#user_#{user.id}" do
|
||||
page.should have_content('Sam')
|
||||
page.find('#team_member_project_access').value.should == access_value(:developer)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -49,8 +55,10 @@ class ProjectTeamManagement < Spinach::FeatureSteps
|
|||
end
|
||||
|
||||
And 'I should see "Sam" in team list as "Reporter"' do
|
||||
within '.reporters' do
|
||||
user = User.find_by_name("Sam")
|
||||
within ".user_#{user.id}" do
|
||||
page.should have_content('Sam')
|
||||
page.find('#team_member_project_access').value.should == access_value(:reporter)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,4 +111,10 @@ class ProjectTeamManagement < Spinach::FeatureSteps
|
|||
click_link('Remove user from team')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def access_value(key)
|
||||
UsersProject.roles_hash[key].to_s
|
||||
end
|
||||
end
|
||||
|
|
7
spec/factories/users_groups.rb
Normal file
7
spec/factories/users_groups.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
FactoryGirl.define do
|
||||
factory :users_group do
|
||||
group_access { UsersGroup::OWNER }
|
||||
group
|
||||
user
|
||||
end
|
||||
end
|
27
spec/models/users_group_spec.rb
Normal file
27
spec/models/users_group_spec.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe UsersGroup do
|
||||
describe "Associations" do
|
||||
it { should belong_to(:group) }
|
||||
it { should belong_to(:user) }
|
||||
end
|
||||
|
||||
describe "Mass assignment" do
|
||||
it { should_not allow_mass_assignment_of(:group_id) }
|
||||
end
|
||||
|
||||
describe "Validation" do
|
||||
let!(:users_group) { create(:users_group) }
|
||||
|
||||
it { should validate_presence_of(:user_id) }
|
||||
it { should validate_uniqueness_of(:user_id).scoped_to(:group_id).with_message(/already exists/) }
|
||||
|
||||
it { should validate_presence_of(:group_id) }
|
||||
it { should ensure_inclusion_of(:group_access).in_array(UsersGroup.group_access_roles.values) }
|
||||
end
|
||||
|
||||
describe "Delegate methods" do
|
||||
it { should respond_to(:user_name) }
|
||||
it { should respond_to(:user_email) }
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue