Commit 3de4271846496fae3b16c316252816dca3f3c354

Authored by Dmitriy Zaporozhets
2 parents c29457a3 655b836b

Merge branch 'feature/add_user_to_projects_in_group-2298' of https://github.com/…

…zzet/gitlabhq into zzet-feature/add_user_to_projects_in_group-2298
app/controllers/admin/groups_controller.rb
1 class Admin::GroupsController < AdminController 1 class Admin::GroupsController < AdminController
2 - before_filter :group, only: [:edit, :show, :update, :destroy, :project_update] 2 + before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update]
3 3
4 def index 4 def index
5 @groups = Group.order('name ASC') 5 @groups = Group.order('name ASC')
@@ -12,6 +12,8 @@ class Admin::GroupsController &lt; AdminController @@ -12,6 +12,8 @@ class Admin::GroupsController &lt; AdminController
12 @projects = @projects.not_in_group(@group) if @group.projects.present? 12 @projects = @projects.not_in_group(@group) if @group.projects.present?
13 @projects = @projects.all 13 @projects = @projects.all
14 @projects.reject!(&:empty_repo?) 14 @projects.reject!(&:empty_repo?)
  15 +
  16 + @users = User.active
15 end 17 end
16 18
17 def new 19 def new
@@ -65,6 +67,11 @@ class Admin::GroupsController &lt; AdminController @@ -65,6 +67,11 @@ class Admin::GroupsController &lt; AdminController
65 redirect_to :back, notice: 'Group was successfully updated.' 67 redirect_to :back, notice: 'Group was successfully updated.'
66 end 68 end
67 69
  70 + def project_teams_update
  71 + @group.add_users_to_project_teams(params[:user_ids], params[:project_access])
  72 + redirect_to [:admin, @group], notice: 'Users was successfully added.'
  73 + end
  74 +
68 def destroy 75 def destroy
69 @group.destroy 76 @group.destroy
70 77
app/controllers/groups_controller.rb
@@ -53,9 +53,16 @@ class GroupsController &lt; ApplicationController @@ -53,9 +53,16 @@ class GroupsController &lt; ApplicationController
53 53
54 if @project 54 if @project
55 @team_member = @project.users_projects.new 55 @team_member = @project.users_projects.new
  56 + else
  57 + @team_member = UsersProject.new
56 end 58 end
57 end 59 end
58 60
  61 + def team_members
  62 + @group.add_users_to_project_teams(params[:user_ids], params[:project_access])
  63 + redirect_to people_group_path(@group), notice: 'Users was successfully added.'
  64 + end
  65 +
59 protected 66 protected
60 67
61 def group 68 def group
app/models/group.rb
@@ -12,6 +12,12 @@ @@ -12,6 +12,12 @@
12 # 12 #
13 13
14 class Group < Namespace 14 class Group < Namespace
  15 + def add_users_to_project_teams(user_ids, project_access)
  16 + projects.each do |project|
  17 + project.add_users_ids_to_team(user_ids, project_access)
  18 + end
  19 + end
  20 +
15 def users 21 def users
16 users = User.joins(:users_projects).where(users_projects: {project_id: project_ids}) 22 users = User.joins(:users_projects).where(users_projects: {project_id: project_ids})
17 users = users << owner 23 users = users << owner
app/models/project.rb
@@ -64,7 +64,9 @@ class Project &lt; ActiveRecord::Base @@ -64,7 +64,9 @@ class Project &lt; ActiveRecord::Base
64 # Validations 64 # Validations
65 validates :owner, presence: true 65 validates :owner, presence: true
66 validates :description, length: { within: 0..2000 } 66 validates :description, length: { within: 0..2000 }
67 - validates :name, presence: true, length: { within: 0..255 } 67 + validates :name, presence: true, length: { within: 0..255 },
  68 + format: { with: Gitlab::Regex.project_name_regex,
  69 + message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
68 validates :path, presence: true, length: { within: 0..255 }, 70 validates :path, presence: true, length: { within: 0..255 },
69 format: { with: Gitlab::Regex.path_regex, 71 format: { with: Gitlab::Regex.path_regex,
70 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } 72 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
@@ -80,6 +82,7 @@ class Project &lt; ActiveRecord::Base @@ -80,6 +82,7 @@ class Project &lt; ActiveRecord::Base
80 scope :public_only, where(private_flag: false) 82 scope :public_only, where(private_flag: false)
81 scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.projects.map(&:id) ) } 83 scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.projects.map(&:id) ) }
82 scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) } 84 scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) }
  85 + scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) }
83 scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") } 86 scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") }
84 scope :personal, ->(user) { where(namespace_id: user.namespace_id) } 87 scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
85 scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } 88 scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
app/models/users_project.rb
@@ -33,6 +33,8 @@ class UsersProject &lt; ActiveRecord::Base @@ -33,6 +33,8 @@ class UsersProject &lt; ActiveRecord::Base
33 33
34 delegate :name, :email, to: :user, prefix: true 34 delegate :name, :email, to: :user, prefix: true
35 35
  36 + scope :in_project, ->(project) { where(project_id: project.id) }
  37 +
36 class << self 38 class << self
37 def import_team(source_project, target_project) 39 def import_team(source_project, target_project)
38 UsersProject.without_repository_callback do 40 UsersProject.without_repository_callback do
app/views/admin/groups/show.html.haml
@@ -44,25 +44,56 @@ @@ -44,25 +44,56 @@
44 %div 44 %div
45 = f.submit 'Change Owner', class: "btn danger" 45 = f.submit 'Change Owner', class: "btn danger"
46 = link_to "Cancel", "#", class: "btn change-owner-cancel-link" 46 = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
47 -%fieldset  
48 - %legend Projects (#{@group.projects.count})  
49 - %table  
50 - %thead 47 +
  48 +- if @group.projects.any?
  49 + %fieldset
  50 + %legend Projects (#{@group.projects.count})
  51 + %table
  52 + %thead
  53 + %tr
  54 + %th Project name
  55 + %th Path
  56 + %th Users
  57 + %th.cred Danger Zone!
  58 + - @group.projects.each do |project|
  59 + %tr
  60 + %td
  61 + = link_to project.name_with_namespace, [:admin, project]
  62 + %td
  63 + %span.monospace= project.path_with_namespace + ".git"
  64 + %td= project.users.count
  65 + %td.bgred
  66 + = link_to 'Transfer project to global namespace', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Remove project from group and move to global namespace. Are you sure?', method: :delete, class: "btn danger small"
  67 +
  68 + = form_tag project_teams_update_admin_group_path(@group), id: "new_team_member", class: "bulk_import", method: :put do
  69 + %table.zebra-striped
  70 + %thead
  71 + %tr
  72 + %th Users
  73 + %th Project Access:
  74 +
  75 + - @group.users.each do |u|
  76 + %tr{class: "user_#{u.id}"}
  77 + %td.name= link_to u.name, admin_user_path(u)
  78 + %td.projects_access
  79 + - u.projects.in_namespace(@group).each do |project|
  80 + - u_p = u.users_projects.in_project(project).first
  81 + %span
  82 + = project.name
  83 + = link_to "(#{ u_p.project_access_human })", edit_admin_team_member_path(u_p)
51 %tr 84 %tr
52 - %th Project name  
53 - %th Path  
54 - %th Users  
55 - %th.cred Danger Zone!  
56 - - @group.projects.each do |project| 85 + %td.input= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
  86 + %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
  87 +
57 %tr 88 %tr
  89 + %td= submit_tag 'Add user to projects in group', class: "btn primary"
58 %td 90 %td
59 - = link_to project.name_with_namespace, [:admin, project]  
60 - %td  
61 - %span.monospace= project.path_with_namespace + ".git"  
62 - %td= project.users.count  
63 - %td.bgred  
64 - = link_to 'Transfer project to global namespace', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Remove project from group and move to global namespace. Are you sure?', method: :delete, class: "btn danger small" 91 + Read more about project permissions
  92 + %strong= link_to "here", help_permissions_path, class: "vlink"
65 93
  94 +- else
  95 + %fieldset
  96 + %legend Group is empty
66 97
67 = form_tag project_update_admin_group_path(@group), class: "bulk_import", method: :put do 98 = form_tag project_update_admin_group_path(@group), class: "bulk_import", method: :put do
68 %fieldset 99 %fieldset
app/views/groups/_new_group_member.html.haml 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 += form_for @team_member, as: :team_member, url: team_members_group_path(@group) do |f|
  2 + %fieldset
  3 + %legend= "New Team member(s) for projects in #{@group.name}"
  4 +
  5 + %h6 1. Choose people you want in the team
  6 + .clearfix
  7 + = f.label :user_ids, "People"
  8 + .input= select_tag(:user_ids, options_from_collection_for_select(User.active, :id, :name), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true})
  9 +
  10 + %h6 2. Set access level for them
  11 + .clearfix
  12 + = f.label :project_access, "Project Access"
  13 + .input= select_tag :project_access, options_for_select(Project.access_options, @team_member.project_access), class: "project-access-select chosen"
  14 +
  15 + .form-actions
  16 + = hidden_field_tag :redirect_to, people_group_path(@group)
  17 + = f.submit 'Add', class: "btn save-btn"
  18 +
app/views/groups/people.html.haml
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
2 .span3 2 .span3
3 = render 'people_filter' 3 = render 'people_filter'
4 .span9 4 .span9
5 - - if @project && can?(current_user, :manage_group, @group)  
6 - = render "new_member" 5 + - if can?(current_user, :manage_group, @group)
  6 + = render (@project ? "new_member" : "new_group_member")
7 .ui-box 7 .ui-box
8 %h5 8 %h5
9 Team 9 Team
config/routes.rb
@@ -47,6 +47,7 @@ Gitlab::Application.routes.draw do @@ -47,6 +47,7 @@ Gitlab::Application.routes.draw do
47 resources :groups, constraints: { id: /[^\/]+/ } do 47 resources :groups, constraints: { id: /[^\/]+/ } do
48 member do 48 member do
49 put :project_update 49 put :project_update
  50 + put :project_teams_update
50 delete :remove_project 51 delete :remove_project
51 end 52 end
52 end 53 end
@@ -102,6 +103,7 @@ Gitlab::Application.routes.draw do @@ -102,6 +103,7 @@ Gitlab::Application.routes.draw do
102 get :merge_requests 103 get :merge_requests
103 get :search 104 get :search
104 get :people 105 get :people
  106 + post :team_members
105 end 107 end
106 end 108 end
107 109
features/admin/groups.feature
1 Feature: Admin Groups 1 Feature: Admin Groups
2 Background: 2 Background:
3 Given I sign in as an admin 3 Given I sign in as an admin
  4 + And I have group with projects
  5 + And Create gitlab user "John"
4 And I visit admin groups page 6 And I visit admin groups page
5 7
6 Scenario: Create a group 8 Scenario: Create a group
@@ -8,3 +10,8 @@ Feature: Admin Groups @@ -8,3 +10,8 @@ Feature: Admin Groups
8 And submit form with new group info 10 And submit form with new group info
9 Then I should be redirected to group page 11 Then I should be redirected to group page
10 And I should see newly created group 12 And I should see newly created group
  13 +
  14 + Scenario: Add user into projects in group
  15 + When I visit admin group page
  16 + When I select user "John" from user list as "Reporter"
  17 + Then I should see "John" in team list in every project as "Reporter"
features/group/group.feature
@@ -17,3 +17,9 @@ Feature: Groups @@ -17,3 +17,9 @@ Feature: Groups
17 Given project from group has merge requests assigned to me 17 Given project from group has merge requests assigned to me
18 When I visit group merge requests page 18 When I visit group merge requests page
19 Then I should see merge requests from this group assigned to me 19 Then I should see merge requests from this group assigned to me
  20 +
  21 + Scenario: I should add user to projects in Group
  22 + Given I have new user "John"
  23 + When I visit group people page
  24 + And I select user "John" from list with role "Reporter"
  25 + Then I should see user "John" in team list
features/steps/admin/admin_groups.rb
@@ -3,10 +3,26 @@ class AdminGroups &lt; Spinach::FeatureSteps @@ -3,10 +3,26 @@ class AdminGroups &lt; Spinach::FeatureSteps
3 include SharedPaths 3 include SharedPaths
4 include SharedActiveTab 4 include SharedActiveTab
5 5
  6 + When 'I visit admin group page' do
  7 + visit admin_group_path(current_group)
  8 + end
  9 +
6 When 'I click new group link' do 10 When 'I click new group link' do
7 click_link "New Group" 11 click_link "New Group"
8 end 12 end
9 13
  14 + And 'I have group with projects' do
  15 + @group = create(:group)
  16 + @project = create(:project, group: @group)
  17 + @event = create(:closed_issue_event, project: @project)
  18 +
  19 + @project.add_access current_user, :admin
  20 + end
  21 +
  22 + And 'Create gitlab user "John"' do
  23 + create(:user, :name => "John")
  24 + end
  25 +
10 And 'submit form with new group info' do 26 And 'submit form with new group info' do
11 fill_in 'group_name', :with => 'gitlab' 27 fill_in 'group_name', :with => 'gitlab'
12 click_button "Create group" 28 click_button "Create group"
@@ -19,5 +35,27 @@ class AdminGroups &lt; Spinach::FeatureSteps @@ -19,5 +35,27 @@ class AdminGroups &lt; Spinach::FeatureSteps
19 Then 'I should be redirected to group page' do 35 Then 'I should be redirected to group page' do
20 current_path.should == admin_group_path(Group.last) 36 current_path.should == admin_group_path(Group.last)
21 end 37 end
  38 +
  39 + When 'I select user "John" from user list as "Reporter"' do
  40 + user = User.find_by_name("John")
  41 + within "#new_team_member" do
  42 + select user.name, :from => "user_ids"
  43 + select "Reporter", :from => "project_access"
  44 + end
  45 + click_button "Add user to projects in group"
  46 + end
  47 +
  48 + Then 'I should see "John" in team list in every project as "Reporter"' do
  49 + user = User.find_by_name("John")
  50 + projects_with_access = find(".user_#{user.id} .projects_access")
  51 + projects_with_access.should have_link("Reporter")
  52 + end
  53 +
  54 + protected
  55 +
  56 + def current_group
  57 + @group ||= Group.first
  58 + end
  59 +
22 end 60 end
23 61
features/steps/group/group.rb
@@ -9,7 +9,7 @@ class Groups &lt; Spinach::FeatureSteps @@ -9,7 +9,7 @@ class Groups &lt; Spinach::FeatureSteps
9 end 9 end
10 10
11 And 'I have group with projects' do 11 And 'I have group with projects' do
12 - @group = create(:group) 12 + @group = create(:group, owner: current_user)
13 @project = create(:project, group: @group) 13 @project = create(:project, group: @group)
14 @event = create(:closed_issue_event, project: @project) 14 @event = create(:closed_issue_event, project: @project)
15 15
@@ -32,6 +32,24 @@ class Groups &lt; Spinach::FeatureSteps @@ -32,6 +32,24 @@ class Groups &lt; Spinach::FeatureSteps
32 end 32 end
33 end 33 end
34 34
  35 + Given 'I have new user "John"' do
  36 + create(:user, name: "John")
  37 + end
  38 +
  39 + And 'I select user "John" from list with role "Reporter"' do
  40 + user = User.find_by_name("John")
  41 + within "#new_team_member" do
  42 + select user.name, :from => "user_ids"
  43 + select "Reporter", :from => "project_access"
  44 + end
  45 + click_button "Add"
  46 + end
  47 +
  48 + Then 'I should see user "John" in team list' do
  49 + projects_with_access = find(".ui-box .well-list")
  50 + projects_with_access.should have_content("John")
  51 + end
  52 +
35 Given 'project from group has issues assigned to me' do 53 Given 'project from group has issues assigned to me' do
36 create :issue, 54 create :issue,
37 project: project, 55 project: project,
features/steps/shared/paths.rb
@@ -21,6 +21,10 @@ module SharedPaths @@ -21,6 +21,10 @@ module SharedPaths
21 visit merge_requests_group_path(current_group) 21 visit merge_requests_group_path(current_group)
22 end 22 end
23 23
  24 + When 'I visit group people page' do
  25 + visit people_group_path(current_group)
  26 + end
  27 +
24 # ---------------------------------------- 28 # ----------------------------------------
25 # Dashboard 29 # Dashboard
26 # ---------------------------------------- 30 # ----------------------------------------
lib/gitlab/regex.rb
@@ -6,6 +6,10 @@ module Gitlab @@ -6,6 +6,10 @@ module Gitlab
6 default_regex 6 default_regex
7 end 7 end
8 8
  9 + def project_name_regex
  10 + default_regex
  11 + end
  12 +
9 def path_regex 13 def path_regex
10 default_regex 14 default_regex
11 end 15 end