Commit c51d3395 authored by Eric Davis's avatar Eric Davis

[#809] Multiple projects can now be assigned to a group or user.

* Added a Javascript autocomplete for searching Projects
* Updated the Users and Groups controllers' #edit_membership method to
  create/update multiple Member records
parent af00598e
......@@ -14,6 +14,7 @@
class AutoCompletesController < ApplicationController
before_filter :find_project, :only => :issues
before_filter :require_admin, :only => :projects
def issues
@issues = []
......@@ -58,6 +59,12 @@ class AutoCompletesController < ApplicationController
@users = user_finder.active.like(params[:q]).find(:all, :limit => 100) - @removed_users
render :layout => false
end
def projects
@principal = Principal.find(params[:id])
@projects = Project.active.like(params[:q]).find(:all, :limit => 100) - @principal.projects
render :layout => false
end
private
......
......@@ -128,8 +128,17 @@ class GroupsController < ApplicationController
def edit_membership
@group = Group.find(params[:id])
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
@membership.save if request.post?
if params[:project_ids] # Multiple memberships, one per project
params[:project_ids].each do |project_id|
@membership = Member.edit_membership(params[:membership_id], params[:membership].merge(:project_id => project_id), @group)
@membership.save if request.post?
end
else # Single membership
@membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
@membership.save if request.post?
end
respond_to do |format|
if @membership.valid?
format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
......
......@@ -197,8 +197,16 @@ class UsersController < ApplicationController
def edit_membership
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save if request.post?
if params[:project_ids] # Multiple memberships, one per project
params[:project_ids].each do |project_id|
@membership = Member.edit_membership(params[:membership_id], params[:membership].merge(:project_id => project_id), @user)
@membership.save if request.post?
end
else # Single membership
@membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
@membership.save if request.post?
end
respond_to do |format|
if @membership.valid?
format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
......
......@@ -290,6 +290,14 @@ module ApplicationHelper
s
end
def projects_check_box_tags(name, projects)
s = ''
projects.each do |project|
s << "<label>#{ check_box_tag name, project.id, false } #{h project}</label>\n"
end
s
end
# Truncates and returns the string as a single line
def truncate_single_line(string, *args)
truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
......
......@@ -82,6 +82,12 @@ class Project < ActiveRecord::Base
named_scope :active, { :conditions => "#{Project.table_name}.status = #{STATUS_ACTIVE}"}
named_scope :all_public, { :conditions => { :is_public => true } }
named_scope :visible, lambda { { :conditions => Project.visible_by(User.current) } }
named_scope :like, lambda {|q|
s = "%#{q.to_s.strip.downcase}%"
{
:conditions => ["LOWER(name) LIKE ?", s]
}
}
def to_liquid
ProjectDrop.new(self)
......
<%= projects_check_box_tags 'project_ids[]', @projects %>
\ No newline at end of file
......@@ -41,5 +41,5 @@
</div>
<div class="splitcontentright">
<%= render :partial => 'members/membership_assignment', :locals => {:principal => @group, :projects => projects, :roles => roles } %>
<%= render :partial => 'members/membership_assignment', :locals => {:principal => @group, :projects => projects - @group.projects, :roles => roles } %>
</div>
<% if projects.any? %>
<fieldset><legend><%=l(:label_project_new)%></legend>
<% remote_form_for(:membership, :url => { :action => 'edit_membership', :id => principal }) do %>
<%= label_tag "membership_project_id", l(:description_choose_project), :class => "hidden-for-sighted" %>
<%= select_tag 'membership[project_id]', options_for_membership_project_select(principal, projects) %>
<p><%= text_field_tag 'project_search', nil, :size => "40" %></p>
<%= observe_field(:project_search,
:frequency => 0.5,
:update => :projects,
:url => { :controller => 'auto_completes', :action => 'projects', :id => principal },
:with => 'q')
%>
<div id="projects">
<%= principals_check_box_tags 'project_ids[]', projects %>
</div>
<p><%= l(:label_role_plural) %>:
<% roles.each do |role| %>
<label><%= check_box_tag 'membership[role_ids][]', role.id %> <%=h role %></label>
......
......@@ -47,5 +47,5 @@
</div>
<div class="splitcontentright">
<%= render :partial => 'members/membership_assignment', :locals => {:principal => @user, :projects => projects, :roles => roles } %>
<%= render :partial => 'members/membership_assignment', :locals => {:principal => @user, :projects => projects - @user.projects, :roles => roles } %>
</div>
......@@ -515,7 +515,7 @@ div#tab-content-members .splitcontentright, div#tab-content-memberships .splitco
div#tab-content-members fieldset, div#tab-content-memberships fieldset, div#tab-content-users fieldset { padding:1em; margin-bottom: 1em; }
div#tab-content-members fieldset legend, div#tab-content-memberships fieldset legend, div#tab-content-users fieldset legend { font-weight: bold; }
div#tab-content-members fieldset label, div#tab-content-memberships fieldset label, div#tab-content-users fieldset label { display: block; }
div#tab-content-members fieldset div, div#tab-content-users fieldset div { max-height: 400px; overflow:auto; }
div#tab-content-members fieldset div, div#tab-content-users fieldset div, div#tab-content-memberships fieldset div { max-height: 400px; overflow:auto; }
table.members td.group { padding-left: 20px; background: url(../images/group.png) no-repeat 0% 50%; }
......
......@@ -142,4 +142,59 @@ class AutoCompletesControllerTest < ActionController::TestCase
end
end
end
context "POST to #projects" do
setup do
# Clear out some fixtures
Project.delete_all
ProjectCustomField.delete_all
end
should 'require admin' do
@request.session[:user_id] = 2
post :projects, {}
assert_response 403
end
context 'with a valid search' do
setup do
@user = User.generate_with_protected!
@projects = [
Project.generate!(:name => "Test"),
Project.generate!(:name => "This is a Test")
]
Project.generate!(:name => "No match")
@request.session[:user_id] = 1
post :projects, {
:id => @user.id,
:q => 'TeST'
}
end
should_assign_to(:principal) { @user }
should_assign_to(:projects) { @projects }
should_render_template :projects
end
context 'with an invalid search' do
setup do
@user = User.generate_with_protected!
Project.generate!(:name => "Test")
@request.session[:user_id] = 1
post :projects, {
:id => @user.id,
:q => 'nothing'
}
end
should_assign_to(:principal) { @user }
should_assign_to(:projects) { [] }
should_render_template :projects
end
end
end
......@@ -95,9 +95,16 @@ class GroupsControllerTest < ActionController::TestCase
end
end
def test_new_membership_with_multiple_projects
assert_difference 'Group.find(10).members.count', 3 do
post :edit_membership, :id => 10, :project_ids => [1,2,3], :membership => { :role_ids => ['1', '2']}
end
end
def test_destroy_membership
assert_difference 'Group.find(10).members.count', -1 do
post :destroy_membership, :id => 10, :membership_id => 6
end
end
end
......@@ -302,6 +302,13 @@ class UsersControllerTest < ActionController::TestCase
assert_equal [2], Member.find(1).role_ids
end
def test_new_membership_with_multiple_projects
assert_difference 'User.find(2).members.count', 2 do
post :edit_membership, :id => 2, :project_ids => [3,6], :membership => { :role_ids => ['1', '2']}
end
end
def test_destroy_membership
post :destroy_membership, :id => 2, :membership_id => 1
assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment