From 82499a4cbfdd9605312322fea80b76f034230b1b Mon Sep 17 00:00:00 2001
From: Andrey Kumanyaev <me@zzet.org>
Date: Sat, 19 Jan 2013 21:11:11 +0400
Subject: [PATCH] Admin teams section added

---
 app/controllers/admin/teams_controller.rb | 104 ++++++++++
 app/views/admin/teams/edit.html.haml      |  28 +++
 app/views/admin/teams/index.html.haml     |  37 ++++
 app/views/admin/teams/new.html.haml       |  21 ++
 app/views/admin/teams/show.html.haml      | 104 ++++++++++
 app/views/layouts/admin.html.haml         |   2 +
 config/routes.rb                          |   8 +
 features/admin/teams.feature              |  73 +++++++
 features/steps/admin/admin_teams.rb       | 222 ++++++++++++++++++++++
 features/steps/shared/paths.rb            |   4 +
 10 files changed, 603 insertions(+)
 create mode 100644 app/controllers/admin/teams_controller.rb
 create mode 100644 app/views/admin/teams/edit.html.haml
 create mode 100644 app/views/admin/teams/index.html.haml
 create mode 100644 app/views/admin/teams/new.html.haml
 create mode 100644 app/views/admin/teams/show.html.haml
 create mode 100644 features/admin/teams.feature
 create mode 100644 features/steps/admin/admin_teams.rb

diff --git a/app/controllers/admin/teams_controller.rb b/app/controllers/admin/teams_controller.rb
new file mode 100644
index 00000000000..fd25d3fe219
--- /dev/null
+++ b/app/controllers/admin/teams_controller.rb
@@ -0,0 +1,104 @@
+class Admin::TeamsController < AdminController
+  before_filter :user_team,
+                only: [ :edit, :show, :update, :destroy,
+                        :delegate_projects, :relegate_project,
+                        :add_members, :remove_member ]
+
+  def index
+    @teams = UserTeam.order('name ASC')
+    @teams = @teams.search(params[:name]) if params[:name].present?
+    @teams = @teams.page(params[:page]).per(20)
+  end
+
+  def show
+    @projects = Project.scoped
+    @projects = @projects.without_team(@team) if @team.projects.any?
+    #@projects.reject!(&:empty_repo?)
+
+    @users = User.active
+    @users = @users.not_in_team(@team) if @team.members.any?
+    @users = UserDecorator.decorate @users
+  end
+
+  def new
+    @team = UserTeam.new
+  end
+
+  def edit
+  end
+
+  def create
+    @team = UserTeam.new(params[:user_team])
+    @team.path = @team.name.dup.parameterize if @team.name
+    @team.owner = current_user
+
+    if @team.save
+      redirect_to admin_team_path(@team), notice: 'UserTeam was successfully created.'
+    else
+      render action: "new"
+    end
+  end
+
+  def update
+    user_team_params = params[:user_team].dup
+    owner_id = user_team_params.delete(:owner_id)
+
+    if owner_id
+      @team.owner = User.find(owner_id)
+    end
+
+    if @team.update_attributes(user_team_params)
+      redirect_to admin_team_path(@team), notice: 'UserTeam was successfully updated.'
+    else
+      render action: "edit"
+    end
+  end
+
+  def destroy
+    @team.destroy
+
+    redirect_to admin_user_teams_path, notice: 'UserTeam was successfully deleted.'
+  end
+
+  def delegate_projects
+    unless params[:project_ids].blank?
+      project_ids = params[:project_ids]
+      access = params[:greatest_project_access]
+      @team.assign_to_projects(project_ids, access)
+    end
+
+    redirect_to admin_team_path(@team), notice: 'Projects was successfully added.'
+  end
+
+  def relegate_project
+    project = params[:project_id]
+    @team.resign_from_project(project)
+
+    redirect_to admin_team_path(@team), notice: 'Project was successfully removed.'
+  end
+
+  def add_members
+    unless params[:user_ids].blank?
+      user_ids = params[:user_ids]
+      access = params[:default_project_access]
+      is_admin = params[:group_admin]
+      @team.add_members(user_ids, access, is_admin)
+    end
+
+    redirect_to admin_team_path(@team), notice: 'Members was successfully added.'
+  end
+
+  def remove_member
+    member = params[:member_id]
+    @team.remove_member(member)
+
+    redirect_to admin_team_path(@team), notice: 'Member was successfully removed.'
+  end
+
+  private
+
+  def user_team
+    @team = UserTeam.find_by_path(params[:id])
+  end
+
+end
diff --git a/app/views/admin/teams/edit.html.haml b/app/views/admin/teams/edit.html.haml
new file mode 100644
index 00000000000..15ec267f45f
--- /dev/null
+++ b/app/views/admin/teams/edit.html.haml
@@ -0,0 +1,28 @@
+%h3.page_title Rename Team
+%hr
+= form_for [:admin, @team] do |f|
+  - if @team.errors.any?
+    .alert-message.block-message.error
+      %span= @team.errors.full_messages.first
+  .clearfix.team_name_holder
+    = f.label :name do
+      Team name is
+    .input
+      = f.text_field :name, placeholder: "Example Team", class: "xxlarge"
+
+
+
+  .clearfix.team_name_holder
+    = f.label :path do
+      %span.cred Team path is
+    .input
+      = f.text_field :path, placeholder: "example-team", class: "xxlarge danger"
+      %ul.cred
+        %li Changing team path can have unintended side effects.
+        %li Renaming team path will rename directory for all related projects
+        %li It will change web url for access team and team projects.
+        %li It will change the git path to repositories under this team.
+
+  .form-actions
+    = f.submit 'Rename team', class: "btn danger"
+    = link_to  'Cancel', admin_teams_path, class: "btn cancel-btn"
diff --git a/app/views/admin/teams/index.html.haml b/app/views/admin/teams/index.html.haml
new file mode 100644
index 00000000000..8b6928e906e
--- /dev/null
+++ b/app/views/admin/teams/index.html.haml
@@ -0,0 +1,37 @@
+%h3.page_title
+  Teams
+  %small
+    simple Teams description
+
+  = link_to 'New Team', new_admin_team_path, class: "btn small right"
+  %br
+
+= form_tag admin_teams_path, method: :get, class: 'form-inline' do
+  = text_field_tag :name, params[:name], class: "xlarge"
+  = submit_tag "Search", class: "btn submit primary"
+
+%table
+  %thead
+    %tr
+      %th
+        Name
+        %i.icon-sort-down
+      %th Path
+      %th Projects
+      %th Members
+      %th Owner
+      %th.cred Danger Zone!
+
+  - @teams.each do |team|
+    %tr
+      %td
+        %strong= link_to team.name, admin_team_path(team)
+      %td= team.path
+      %td= team.projects.count
+      %td= team.members.count
+      %td
+        = link_to team.owner.name, admin_user_path(team.owner_id)
+      %td.bgred
+        = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn small"
+        = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn small danger"
+= paginate @teams, theme: "admin"
diff --git a/app/views/admin/teams/new.html.haml b/app/views/admin/teams/new.html.haml
new file mode 100644
index 00000000000..c936b66b32e
--- /dev/null
+++ b/app/views/admin/teams/new.html.haml
@@ -0,0 +1,21 @@
+%h3.page_title New Team
+%hr
+= form_for @team, url: admin_teams_path do |f|
+  - if @team.errors.any?
+    .alert-message.block-message.error
+      %span= @team.errors.full_messages.first
+  .clearfix
+    = f.label :name do
+      Team name is
+    .input
+      = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
+      &nbsp;
+      = f.submit 'Create team', class: "btn primary"
+  %hr
+  .padded
+    %ul
+      %li Team is kind of directory for several projects
+      %li All created teams are private
+      %li People within a team see only projects they have access to
+      %li All projects of team will be stored in team directory
+      %li You will be able to move existing projects into team
diff --git a/app/views/admin/teams/show.html.haml b/app/views/admin/teams/show.html.haml
new file mode 100644
index 00000000000..0f47717ae0e
--- /dev/null
+++ b/app/views/admin/teams/show.html.haml
@@ -0,0 +1,104 @@
+%h3.page_title
+  Team: #{@team.name}
+
+%br
+%table.zebra-striped
+  %thead
+    %tr
+      %th Team
+      %th
+  %tr
+    %td
+      %b
+        Name:
+    %td
+      = @team.name
+      &nbsp;
+      = link_to edit_admin_team_path(@team), class: "btn btn-small right" do
+        %i.icon-edit
+        Rename
+  %tr
+    %td
+      %b
+        Owner:
+    %td
+      = @team.owner.name
+      .right
+        = link_to "#", class: "btn btn-small change-owner-link" do
+          %i.icon-edit
+          Change owner
+
+  %tr.change-owner-holder.hide
+    %td.bgred
+      %b.cred
+        New Owner:
+    %td.bgred
+      = form_for @team, url: admin_team_path(@team) do |f|
+        = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
+        %div
+          = f.submit 'Change Owner', class: "btn danger"
+          = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
+
+%fieldset
+  %legend Members (#{@team.members.count})
+  = form_tag add_members_admin_team_path(@team), id: "team_members", class: "bulk_import", method: :post  do
+    %table#members_list
+      %thead
+        %tr
+          %th User name
+          %th Default project access
+          %th Team access
+          %th.cred Danger Zone!
+      - @team.members.each do |member|
+        %tr.member
+          %td
+            = link_to [:admin, member] do
+              = member.name
+              %small= "(#{member.email})"
+          %td= @team.human_default_projects_access(member)
+          %td= @team.admin?(member) ? "Admin" : "Member"
+          %td.bgred
+            = link_to 'Remove', remove_member_admin_team_path(@team, member_id: member.id), confirm: 'Remove project from team and move to global namespace. Are you sure?', method: :delete, class: "btn danger small"
+      %tr
+        %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_email), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
+        %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
+        %td
+          %span= check_box_tag :group_admin
+          %span Admin?
+        %td= submit_tag 'Add', class: "btn primary", id: :add_members_to_team
+
+%fieldset
+  %legend Projects (#{@team.projects.count})
+  = form_tag delegate_projects_admin_team_path(@team), id: "assign_projects", class: "bulk_import", method: :post  do
+    %table#projects_list
+      %thead
+        %tr
+          %th Project name
+          %th Max access
+          %th.cred Danger Zone!
+      - @team.projects.each do |project|
+        %tr.project
+          %td
+            = link_to project.name_with_namespace, [:admin, project]
+          %td
+            %span= @team.human_max_project_access(project)
+          %td.bgred
+            = link_to 'Relegate', relegate_project_admin_team_path(@team, project_id: project.id), confirm: 'Remove project from team and move to global namespace. Are you sure?', method: :delete, class: "btn danger small"
+      %tr
+        %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
+        %td= select_tag :greatest_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
+        %td= submit_tag 'Add', class: "btn primary", id: :assign_projects_to_team
+
+:javascript
+  $(function(){
+    var modal = $('.change-owner-holder');
+    $('.change-owner-link').bind("click", function(){
+      $(this).hide();
+      modal.show();
+    });
+    $('.change-owner-cancel-link').bind("click", function(){
+      modal.hide();
+      $('.change-owner-link').show();
+    })
+  })
+
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index a60e7febe76..28626b9c682 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -10,6 +10,8 @@
           = link_to "Stats", admin_root_path
         = nav_link(controller: :projects) do
           = link_to "Projects", admin_projects_path
+        = nav_link(controller: :teams) do
+          = link_to "Teams", admin_teams_path
         = nav_link(controller: :groups) do
           = link_to "Groups", admin_groups_path
         = nav_link(controller: :users) do
diff --git a/config/routes.rb b/config/routes.rb
index 6d7e615187d..d364f80551e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -69,6 +69,14 @@ Gitlab::Application.routes.draw do
         put :team_update
       end
     end
+    resources :teams do #, constraints: { id: /[^\/]+/ } do end
+      member do
+        post :delegate_projects
+        delete :relegate_project
+        post :add_members
+        delete :remove_member
+      end
+    end
     resources :team_members, only: [:edit, :update, :destroy]
     resources :hooks, only: [:index, :create, :destroy] do
       get :test
diff --git a/features/admin/teams.feature b/features/admin/teams.feature
new file mode 100644
index 00000000000..e070a90ae02
--- /dev/null
+++ b/features/admin/teams.feature
@@ -0,0 +1,73 @@
+Feature: Admin Teams
+  Background:
+    Given I sign in as an admin
+    #And there are projects in system
+    #And system has users
+    #And I have own project
+    And Create gitlab user "John"
+
+  Scenario: Create a team
+    When I visit admin teams page
+    And I click new team link
+    And submit form with new team info
+    Then I should be redirected to team page
+    And I should see newly created team
+
+  Scenario: Add user to team
+    When I visit admin teams page
+    When I have clean "HardCoders" team
+    And I visit "HardCoders" team page
+    #Then I should see only me in members table
+    When I select user "John" from user list as "Developer"
+    And submit form with new team member info
+    Then I should see "John" in teams members list as "Developer"
+    When I visit "John" user admin page
+    Then I should see "HardCoders" team in teams table
+
+  Scenario: Assign team to existing project
+    When I visit admin teams page
+    When I have "HardCoders" team with "John" member with "Developer" role
+    When I have "Shop" project
+    And I visit "HardCoders" team page
+    Then I should see empty projects table
+    When I select project "Shop" with max access "Reporter"
+    And submit form with new team project info
+    Then I should see "Shop" project in projects list
+    When I visit "Shop" project admin page
+    Then I should see "John" user with role "Reporter" in team table
+
+  Scenario: Add user to team with ptojects
+    When I visit admin teams page
+    When I have "HardCoders" team with "John" member with "Developer" role
+    And "HardCoders" team assigned to "Shop" project with "Developer" max role access
+    When I have gitlab user "Jimm"
+    And I visit "HardCoders" team page
+    Then I should see members table without "Jimm" member
+    When I select user "Jimm" ub team members list as "Master"
+    And submit form with new team member info
+    Then I should see "Jimm" in teams members list as "Master"
+
+  Scenario: Remove member from team
+    Given I have users team "HardCoders"
+    And gitlab user "John" is a member "HardCoders" team
+    And gitlab user "Jimm" is a member "HardCoders" team
+    And "HardCoders" team is assigned to "Shop" project
+    When I visit admin teams page
+    When I visit "HardCoders" team admin page
+    Then I shoould see "John" in members list
+    And I should see "Jimm" in members list
+    And I should see "Shop" in projects list
+    When I click on remove "Jimm" user link
+    Then I should be redirected to "HardCoders" team admin page
+    And I should not to see "Jimm" user in members list
+
+  Scenario: Remove project from team
+    Given I have users team "HardCoders"
+    And gitlab user "John" is a member "HardCoders" team
+    And gitlab user "Jimm" is a member "HardCoders" team
+    And "HardCoders" team is assigned to "Shop" project
+    When I visit admin teams page
+    When I visit "HardCoders" team admin page
+    Then I should see "Shop" project in projects list
+    When I click on "Relegate" link on "Shop" project
+    Then I should see projects liston team page without "Shop" project
diff --git a/features/steps/admin/admin_teams.rb b/features/steps/admin/admin_teams.rb
new file mode 100644
index 00000000000..7bb1dacabcb
--- /dev/null
+++ b/features/steps/admin/admin_teams.rb
@@ -0,0 +1,222 @@
+class AdminTeams < Spinach::FeatureSteps
+  include SharedAuthentication
+  include SharedPaths
+  include SharedActiveTab
+  include SharedAdmin
+
+  And 'I have own project' do
+    create :project
+  end
+
+  And 'Create gitlab user "John"' do
+    @user = create(:user, :name => "John")
+  end
+
+  And 'I click new team link' do
+    click_link "New Team"
+  end
+
+  And 'submit form with new team info' do
+    fill_in 'user_team_name', with: 'gitlab'
+    click_button 'Create team'
+  end
+
+  Then 'I should be redirected to team page' do
+    current_path.should == admin_team_path(UserTeam.last)
+  end
+
+  And 'I should see newly created team' do
+    page.should have_content "Team: gitlab"
+  end
+
+  When 'I visit admin teams page' do
+    visit admin_teams_path
+  end
+
+  When 'I have clean "HardCoders" team' do
+    @team = create :user_team, name: "HardCoders", owner: current_user
+  end
+
+  And 'I visit "HardCoders" team page' do
+    visit admin_team_path(UserTeam.find_by_name("HardCoders"))
+  end
+
+  Then 'I should see only me in members table' do
+    members_list = find("#members_list .member")
+    members_list.should have_content(current_user.name)
+    members_list.should have_content(current_user.email)
+  end
+
+  When 'I select user "John" from user list as "Developer"' do
+    @user ||= User.find_by_name("John")
+    within "#team_members" do
+      select user.name, :from => "user_ids"
+      select "Developer", :from => "default_project_access"
+    end
+  end
+
+  And 'submit form with new team member info' do
+    click_button 'add_members_to_team'
+  end
+
+  Then 'I should see "John" in teams members list as "Developer"' do
+    @user ||= User.find_by_name("John")
+    find_in_list("#members_list .member", user).must_equal true
+  end
+
+  When 'I visit "John" user admin page' do
+    pending 'step not implemented'
+  end
+
+  Then 'I should see "HardCoders" team in teams table' do
+    pending 'step not implemented'
+  end
+
+  When 'I have "HardCoders" team with "John" member with "Developer" role' do
+    @team = create :user_team, name: "HardCoders", owner: current_user
+    @user ||= User.find_by_name("John")
+    @team.add_member(@user, UserTeam.access_roles["Developer"], group_admin: false)
+  end
+
+  When 'I have "Shop" project' do
+    @project = create :project, name: "Shop"
+  end
+
+  Then 'I should see empty projects table' do
+    projects_list = find("#projects_list")
+    projects_list.has_content?("Relegate").must_equal false
+  end
+
+  When 'I select project "Shop" with max access "Reporter"' do
+    @project ||= Project.find_by_name("Shop")
+    within "#assign_projects" do
+      select @project.name, :from => "project_ids"
+      select "Reporter", :from => "greatest_project_access"
+    end
+
+  end
+
+  And 'submit form with new team project info' do
+    click_button 'assign_projects_to_team'
+  end
+
+  Then 'I should see "Shop" project in projects list' do
+    project = Project.find_by_name("Shop")
+    find_in_list("#projects_list .project", project).must_equal true
+  end
+
+  When 'I visit "Shop" project admin page' do
+    project = Project.find_by_name("Shop")
+    visit admin_project_path(project)
+  end
+
+  And '"HardCoders" team assigned to "Shop" project with "Developer" max role access' do
+    @team = UserTeam.find_by_name("HardCoders")
+    @project = create :project, name: "Shop"
+    @team.assign_to_project(@project, UserTeam.access_roles["Developer"])
+  end
+
+  When 'I have gitlab user "Jimm"' do
+    create :user, name: "Jimm"
+  end
+
+  Then 'I should see members table without "Jimm" member' do
+    user = User.find_by_name("Jimm")
+    find_in_list("#members_list .member", user).must_equal false
+  end
+
+  When 'I select user "Jimm" ub team members list as "Master"' do
+    user = User.find_by_name("Jimm")
+    within "#team_members" do
+      select user.name, :from => "user_ids"
+      select "Developer", :from => "default_project_access"
+    end
+  end
+
+  Then 'I should see "Jimm" in teams members list as "Master"' do
+    user = User.find_by_name("Jimm")
+    find_in_list("#members_list .member", user).must_equal true
+  end
+
+  Given 'I have users team "HardCoders"' do
+    @team = create :user_team, name: "HardCoders"
+  end
+
+  And 'gitlab user "John" is a member "HardCoders" team' do
+    @team = UserTeam.find_by_name("HardCoders")
+    @user = User.find_by_name("John")
+    @user = create :user, name: "John" unless @user
+    @team.add_member(@user, UserTeam.access_roles["Master"], group_admin: false)
+  end
+
+  And 'gitlab user "Jimm" is a member "HardCoders" team' do
+    @team = UserTeam.find_by_name("HardCoders")
+    @user = User.find_by_name("Jimm")
+    @user = create :user, name: "Jimm" unless @user
+    @team.add_member(@user, UserTeam.access_roles["Master"], group_admin: false)
+  end
+
+  And '"HardCoders" team is assigned to "Shop" project' do
+    @team = UserTeam.find_by_name("HardCoders")
+    @project = create :project, name: "Shop"
+    @team.assign_to_project(@project, UserTeam.access_roles["Developer"])
+  end
+
+  When 'I visit "HardCoders" team admin page' do
+    visit admin_team_path(UserTeam.find_by_name("HardCoders"))
+  end
+
+  Then 'I shoould see "John" in members list' do
+    user = User.find_by_name("John")
+    find_in_list("#members_list .member", user).must_equal true
+  end
+
+  And 'I should see "Jimm" in members list' do
+    user = User.find_by_name("Jimm")
+    find_in_list("#members_list .member", user).must_equal true
+  end
+
+  And 'I should see "Shop" in projects list' do
+
+  end
+
+  When 'I click on remove "Jimm" user link' do
+
+  end
+
+  Then 'I should be redirected to "HardCoders" team admin page' do
+    current_path.should admin_team_peth(UserTeam.find_by_name("HardCoders"))
+  end
+
+  And 'I should not to see "Jimm" user in members list' do
+
+  end
+
+  When 'I click on "Relegate" link on "Shop" project' do
+
+  end
+
+  Then 'I should see projects liston team page without "Shop" project' do
+
+  end
+
+  Then 'I should see "John" user with role "Reporter" in team table' do
+    user = User.find_by_name("John")
+    find_in_list(".team_members", user).must_equal true
+  end
+
+  protected
+
+  def current_team
+    @team ||= Team.first
+  end
+
+  def find_in_list(selector, item)
+    members_list = all(selector)
+    entered = false
+    members_list.each do |member_item|
+      entered = true if member_item.has_content?(item.name)
+    end
+    entered
+  end
+end
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index c046c4e63e6..e397ff87f41 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -105,6 +105,10 @@ module SharedPaths
     visit admin_groups_path
   end
 
+  When 'I visit admin teams page' do
+    visit admin_teams_path
+  end
+
   # ----------------------------------------
   # Generic Project
   # ----------------------------------------
-- 
GitLab