summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2013-06-18 19:34:32 +0300
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2013-06-18 19:34:32 +0300
commit30d2d2381f608b7c926747d61a9231aa417ea5da (patch)
tree16b5f2d3452210b2be232b0bae0ddfbb968ec460
parentcc5440e82a396fe4967a0b31322d9bb67ee70057 (diff)
parent60bb35161741338e585feedf09538f5935bba271 (diff)
downloadgitlab-ce-30d2d2381f608b7c926747d61a9231aa417ea5da.tar.gz
Merge branch 'feature/users_groups' into 6-0-dev
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee2
-rw-r--r--app/assets/javascripts/groups.js.coffee6
-rw-r--r--app/assets/stylesheets/gitlab_bootstrap/lists.scss6
-rw-r--r--app/controllers/admin/groups_controller.rb4
-rw-r--r--app/controllers/dashboard_controller.rb5
-rw-r--r--app/controllers/graphs_controller.rb2
-rw-r--r--app/controllers/groups_controller.rb19
-rw-r--r--app/controllers/issues_controller.rb2
-rw-r--r--app/controllers/team_members_controller.rb6
-rw-r--r--app/controllers/users_controller.rb2
-rw-r--r--app/controllers/users_groups_controller.rb40
-rw-r--r--app/helpers/groups_helper.rb4
-rw-r--r--app/models/ability.rb6
-rw-r--r--app/models/group.rb30
-rw-r--r--app/models/project_team.rb38
-rw-r--r--app/models/user.rb10
-rw-r--r--app/models/users_group.rb42
-rw-r--r--app/models/users_project.rb4
-rw-r--r--app/services/system_hooks_service.rb2
-rw-r--r--app/views/admin/groups/show.html.haml39
-rw-r--r--app/views/groups/_new_group_member.html.haml14
-rw-r--r--app/views/groups/_new_member.html.haml18
-rw-r--r--app/views/groups/_people_filter.html.haml16
-rw-r--r--app/views/groups/people.html.haml30
-rw-r--r--app/views/issues/_form.html.haml2
-rw-r--r--app/views/issues/_issues.html.haml4
-rw-r--r--app/views/projects/_settings_nav.html.haml2
-rw-r--r--app/views/team_members/_group_members.html.haml10
-rw-r--r--app/views/team_members/_team.html.haml13
-rw-r--r--app/views/team_members/_team_member.html.haml34
-rw-r--r--app/views/team_members/index.html.haml61
-rw-r--r--app/views/users_groups/_users_group.html.haml16
-rw-r--r--config/routes.rb4
-rw-r--r--db/migrate/20130617095603_create_users_groups.rb11
-rw-r--r--db/schema.rb78
-rw-r--r--features/steps/admin/admin_groups.rb2
-rw-r--r--features/steps/group/group.rb6
-rw-r--r--features/steps/project/project_active_tab.rb4
-rw-r--r--features/steps/project/project_team_management.rb20
-rw-r--r--spec/factories/users_groups.rb7
-rw-r--r--spec/models/users_group_spec.rb27
41 files changed, 396 insertions, 252 deletions
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index fb149b7f677..ce53d14d671 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -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()
diff --git a/app/assets/javascripts/groups.js.coffee b/app/assets/javascripts/groups.js.coffee
new file mode 100644
index 00000000000..c0ffccd8f70
--- /dev/null
+++ b/app/assets/javascripts/groups.js.coffee
@@ -0,0 +1,6 @@
+class GroupMembers
+ constructor: ->
+ $('li.users_group').bind 'ajax:success', ->
+ $(this).fadeOut()
+
+@GroupMembers = GroupMembers
diff --git a/app/assets/stylesheets/gitlab_bootstrap/lists.scss b/app/assets/stylesheets/gitlab_bootstrap/lists.scss
index e661e02623e..0fa4da44de5 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/lists.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/lists.scss
@@ -16,6 +16,12 @@
color: #888;
}
+ &.unstyled {
+ &:hover {
+ background: none;
+ }
+ }
+
&.smoke { background-color: #f5f5f5; }
&:hover {
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index c38461c89db..6e6c8d54e29 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -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.'
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index b74c22b1547..9b3f59600c6 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -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
diff --git a/app/controllers/graphs_controller.rb b/app/controllers/graphs_controller.rb
index 5ae9c15c0f7..6c2ac5fcbf4 100644
--- a/app/controllers/graphs_controller.rb
+++ b/app/controllers/graphs_controller.rb
@@ -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
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index e6559b8d8fe..f44bc10cf39 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -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)
diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index ba92ba2bdae..65e72792924 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -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|
diff --git a/app/controllers/team_members_controller.rb b/app/controllers/team_members_controller.rb
index 35aa315dac1..095d7a3af55 100644
--- a/app/controllers/team_members_controller.rb
+++ b/app/controllers/team_members_controller.rb
@@ -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
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 4947c33f959..028af8ff59f 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -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
diff --git a/app/controllers/users_groups_controller.rb b/app/controllers/users_groups_controller.rb
new file mode 100644
index 00000000000..ebc79d621a4
--- /dev/null
+++ b/app/controllers/users_groups_controller.rb
@@ -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
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 283119bc24c..2ffbff7af8d 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -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
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 0b6314cd555..70a4435e699 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -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
diff --git a/app/models/group.rb b/app/models/group.rb
index 17671c3defe..0593d9cd4bb 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -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
- )
+ after_create :add_owner
+
+ def human_name
+ name
end
- def users
- users = User.joins(:users_projects).where(users_projects: {project_id: project_ids})
- users = users << owner
- users.uniq
+ def owners
+ @owners ||= (users_groups.owners.map(&:user) << owner)
end
- def human_name
- name
+ 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
- def truncate_teams
- UsersProject.truncate_teams(project_ids)
+ private
+
+ def add_owner
+ self.add_users([owner.id], UsersGroup::OWNER)
end
end
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index f3e5c0e5354..20c6690e80b 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -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
diff --git a/app/models/user.rb b/app/models/user.rb
index 6de8d2d4c39..904d2919429 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -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
diff --git a/app/models/users_group.rb b/app/models/users_group.rb
new file mode 100644
index 00000000000..0cb26854f9c
--- /dev/null
+++ b/app/models/users_group.rb
@@ -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
diff --git a/app/models/users_project.rb b/app/models/users_project.rb
index 935ecede42c..4c58b009380 100644
--- a/app/models/users_project.rb
+++ b/app/models/users_project.rb
@@ -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
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index a52d13d5237..01ddf99e97b 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -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
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 9c4b91b1bfa..3de0091801f 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -49,10 +49,23 @@
%strong
= @group.created_at.stamp("March 1, 1999")
+ .ui-box
+ %h5.title
+ Projects
+ %small
+ (#{@group.projects.count})
+ %ul.well-list
+ - @group.projects.sort_by(&:name).each do |project|
+ %li
+ %strong
+ = 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 to Group projects:
+ Add user(s):
.ui-box-body.form-holder
%p.light
Read more about project permissions
@@ -64,30 +77,18 @@
%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"
+ = submit_tag 'Add users into group', class: "btn btn-create"
.ui-box
%h5.title
- Users from Group projects
+ Users from #{@group.name} Group
%small
- (#{@group.users.count})
+ (#{@group.users_groups.count})
%ul.well-list
- - @group.users.sort_by(&:name).each do |user|
+ - @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
- = pluralize user.authorized_projects.in_namespace(@group).count, 'project'
+ = member.human_access
- .span6
- .ui-box
- %h5.title
- Projects
- %small
- (#{@group.projects.count})
- %ul.well-list
- - @group.projects.sort_by(&:name).each do |project|
- %li
- %strong
- = link_to project.name_with_namespace, [:admin, project]
- %span.pull-right.light
- %span.monospace= project.path_with_namespace + ".git"
diff --git a/app/views/groups/_new_group_member.html.haml b/app/views/groups/_new_group_member.html.haml
index 78db1c23320..786a52a6011 100644
--- a/app/views/groups/_new_group_member.html.haml
+++ b/app/views/groups/_new_group_member.html.haml
@@ -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"
diff --git a/app/views/groups/_new_member.html.haml b/app/views/groups/_new_member.html.haml
deleted file mode 100644
index 4a762ed39ed..00000000000
--- a/app/views/groups/_new_member.html.haml
+++ /dev/null
@@ -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"
-
diff --git a/app/views/groups/_people_filter.html.haml b/app/views/groups/_people_filter.html.haml
deleted file mode 100644
index ee63743eb4f..00000000000
--- a/app/views/groups/_people_filter.html.haml
+++ /dev/null
@@ -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'
-
diff --git a/app/views/groups/people.html.haml b/app/views/groups/people.html.haml
index 3e4eb082f56..5b595ad3584 100644
--- a/app/views/groups/people.html.haml
+++ b/app/views/groups/people.html.haml
@@ -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
diff --git a/app/views/issues/_form.html.haml b/app/views/issues/_form.html.haml
index 57460e623ab..38aea6f06dc 100644
--- a/app/views/issues/_form.html.haml
+++ b/app/views/issues/_form.html.haml
@@ -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
&nbsp;
= link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
diff --git a/app/views/issues/_issues.html.haml b/app/views/issues/_issues.html.haml
index cc8d8d9cae2..68598684f90 100644
--- a/app/views/issues/_issues.html.haml
+++ b/app/views/issues/_issues.html.haml
@@ -7,7 +7,7 @@
%span.update_issues_text Update selected issues with &nbsp;
.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"
diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml
index 346bbd2daf3..272c5e4402f 100644
--- a/app/views/projects/_settings_nav.html.haml
+++ b/app/views/projects/_settings_nav.html.haml
@@ -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
diff --git a/app/views/team_members/_group_members.html.haml b/app/views/team_members/_group_members.html.haml
new file mode 100644
index 00000000000..7d9333d38e6
--- /dev/null
+++ b/app/views/team_members/_group_members.html.haml
@@ -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
diff --git a/app/views/team_members/_team.html.haml b/app/views/team_members/_team.html.haml
index 4ff170ac86e..2a663bd9941 100644
--- a/app/views/team_members/_team.html.haml
+++ b/app/views/team_members/_team.html.haml
@@ -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
diff --git a/app/views/team_members/_team_member.html.haml b/app/views/team_members/_team_member.html.haml
index d829a79213c..4bd4fca6718 100644
--- a/app/views/team_members/_team_member.html.haml
+++ b/app/views/team_members/_team_member.html.haml
@@ -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
+ .pull-right
+ - if allow_admin
+ .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"
+ &nbsp;
+ = 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
- .span4.pull-right
- - if allow_admin
- .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
diff --git a/app/views/team_members/index.html.haml b/app/views/team_members/index.html.haml
index c0f7ee4330d..bae7215323c 100644
--- a/app/views/team_members/index.html.haml
+++ b/app/views/team_members/index.html.haml
@@ -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?
- %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
-
-
+- 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
diff --git a/app/views/users_groups/_users_group.html.haml b/app/views/users_groups/_users_group.html.haml
new file mode 100644
index 00000000000..31a829952ac
--- /dev/null
+++ b/app/views/users_groups/_users_group.html.haml
@@ -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
diff --git a/config/routes.rb b/config/routes.rb
index 39c79635c40..b431cb23c83 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -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
#
diff --git a/db/migrate/20130617095603_create_users_groups.rb b/db/migrate/20130617095603_create_users_groups.rb
new file mode 100644
index 00000000000..2efc04f1151
--- /dev/null
+++ b/db/migrate/20130617095603_create_users_groups.rb
@@ -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
diff --git a/db/schema.rb b/db/schema.rb
index c3c751e3a19..415e9723456 100644
--- a/db/schema.rb
+++ b/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
@@ -262,53 +259,60 @@ ActiveRecord::Schema.define(:version => 20130614132337) do
end
create_table "users", :force => true do |t|
- t.string "email", :default => "", :null => false
- t.string "encrypted_password", :default => "", :null => false
+ t.string "email", :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"
- t.integer "sign_in_count", :default => 0
+ t.integer "sign_in_count", :default => 0
t.datetime "current_sign_in_at"
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
- t.string "skype", :default => "", :null => false
- t.string "linkedin", :default => "", :null => false
- t.string "twitter", :default => "", :null => false
+ t.boolean "admin", :default => false, :null => false
+ t.integer "projects_limit", :default => 10
+ t.string "skype", :default => "", :null => false
+ t.string "linkedin", :default => "", :null => false
+ t.string "twitter", :default => "", :null => false
t.string "authentication_token"
- t.integer "theme_id", :default => 1, :null => false
+ t.integer "theme_id", :default => 1, :null => false
t.string "bio"
- t.integer "failed_attempts", :default => 0
+ t.integer "failed_attempts", :default => 0
t.datetime "locked_at"
t.string "extern_uid"
t.string "provider"
t.string "username"
- t.boolean "can_create_group", :default => true, :null => false
- t.boolean "can_create_team", :default => true, :null => false
+ t.boolean "can_create_group", :default => true, :null => false
+ t.boolean "can_create_team", :default => true, :null => false
t.string "state"
- t.integer "color_scheme_id", :default => 1, :null => false
- t.integer "notification_level", :default => 1, :null => false
+ t.integer "color_scheme_id", :default => 1, :null => false
+ t.integer "notification_level", :default => 1, :null => false
t.datetime "password_expires_at"
t.integer "created_by_id"
end
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
diff --git a/features/steps/admin/admin_groups.rb b/features/steps/admin/admin_groups.rb
index d780d9c96d9..2f98b8100dc 100644
--- a/features/steps/admin/admin_groups.rb
+++ b/features/steps/admin/admin_groups.rb
@@ -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
diff --git a/features/steps/group/group.rb b/features/steps/group/group.rb
index 102bc440d82..a6b9aa606c6 100644
--- a/features/steps/group/group.rb
+++ b/features/steps/group/group.rb
@@ -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
diff --git a/features/steps/project/project_active_tab.rb b/features/steps/project/project_active_tab.rb
index 5170ab82e8a..a0ee32b9ec0 100644
--- a/features/steps/project/project_active_tab.rb
+++ b/features/steps/project/project_active_tab.rb
@@ -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
diff --git a/features/steps/project/project_team_management.rb b/features/steps/project/project_team_management.rb
index ffd2aa24676..7e9533de8f6 100644
--- a/features/steps/project/project_team_management.rb
+++ b/features/steps/project/project_team_management.rb
@@ -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
diff --git a/spec/factories/users_groups.rb b/spec/factories/users_groups.rb
new file mode 100644
index 00000000000..cb272e35c92
--- /dev/null
+++ b/spec/factories/users_groups.rb
@@ -0,0 +1,7 @@
+FactoryGirl.define do
+ factory :users_group do
+ group_access { UsersGroup::OWNER }
+ group
+ user
+ end
+end
diff --git a/spec/models/users_group_spec.rb b/spec/models/users_group_spec.rb
new file mode 100644
index 00000000000..c802b8213d5
--- /dev/null
+++ b/spec/models/users_group_spec.rb
@@ -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