Commit 3de4271846496fae3b16c316252816dca3f3c354
Exists in
master
and in
4 other branches
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
Showing
15 changed files
with
173 additions
and
20 deletions
Show diff stats
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 < AdminController | @@ -12,6 +12,8 @@ class Admin::GroupsController < 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 < AdminController | @@ -65,6 +67,11 @@ class Admin::GroupsController < 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 < ApplicationController | @@ -53,9 +53,16 @@ class GroupsController < 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 < ActiveRecord::Base | @@ -64,7 +64,9 @@ class Project < 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 < ActiveRecord::Base | @@ -80,6 +82,7 @@ class Project < 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 < ActiveRecord::Base | @@ -33,6 +33,8 @@ class UsersProject < 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 |
| @@ -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 < Spinach::FeatureSteps | @@ -3,10 +3,26 @@ class AdminGroups < 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 < Spinach::FeatureSteps | @@ -19,5 +35,27 @@ class AdminGroups < 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 < Spinach::FeatureSteps | @@ -9,7 +9,7 @@ class Groups < 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 < Spinach::FeatureSteps | @@ -32,6 +32,24 @@ class Groups < 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