From 7dccf9fda6f30d8f4b0c5eaad9f6e2a1e67cd643 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Sun, 10 May 2009 10:54:31 +0000 Subject: [PATCH] Allows multiple roles on the same project (#706). Prerequisite for user groups feature. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2726 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/controllers/application.rb | 4 -- app/controllers/issues_controller.rb | 2 +- app/controllers/members_controller.rb | 7 ++- app/controllers/projects_controller.rb | 2 +- app/controllers/queries_controller.rb | 4 +- app/controllers/roles_controller.rb | 8 +-- app/controllers/users_controller.rb | 18 ++++-- app/helpers/application_helper.rb | 4 -- app/models/issue.rb | 2 +- app/models/issue_status.rb | 32 ++++++---- app/models/member.rb | 22 ++++--- app/models/member_role.rb | 27 +++++++++ app/models/project.rb | 6 +- app/models/role.rb | 9 ++- app/models/user.rb | 24 ++++---- app/views/account/show.rhtml | 2 +- app/views/projects/settings/_members.rhtml | 40 ++++++++----- app/views/queries/_form.rhtml | 2 +- app/views/users/_memberships.rhtml | 60 ++++++++++++------- app/views/workflows/edit.rhtml | 2 +- .../20090503121501_create_member_roles.rb | 12 ++++ .../20090503121505_populate_member_roles.rb | 11 ++++ .../20090503121510_drop_members_role_id.rb | 9 +++ public/stylesheets/application.css | 14 +++-- test/fixtures/member_roles.yml | 23 +++++++ test/fixtures/members.yml | 5 -- .../functional/attachments_controller_test.rb | 2 +- test/functional/boards_controller_test.rb | 2 +- test/functional/documents_controller_test.rb | 2 +- .../issue_categories_controller_test.rb | 2 +- .../issue_relations_controller_test.rb | 1 + test/functional/issues_controller_test.rb | 1 + test/functional/journals_controller_test.rb | 2 +- .../mail_handler_controller_test.rb | 2 +- test/functional/members_controller_test.rb | 10 ++-- test/functional/messages_controller_test.rb | 2 +- test/functional/news_controller_test.rb | 2 +- test/functional/projects_controller_test.rb | 2 +- test/functional/queries_controller_test.rb | 2 +- .../repositories_bazaar_controller_test.rb | 2 +- .../repositories_controller_test.rb | 2 +- .../repositories_darcs_controller_test.rb | 2 +- .../repositories_git_controller_test.rb | 2 +- .../repositories_mercurial_controller_test.rb | 2 +- ...repositories_subversion_controller_test.rb | 2 +- test/functional/roles_controller_test.rb | 22 +++---- test/functional/search_controller_test.rb | 2 +- test/functional/timelog_controller_test.rb | 2 +- test/functional/users_controller_test.rb | 6 +- test/functional/versions_controller_test.rb | 2 +- test/functional/watchers_controller_test.rb | 2 +- test/functional/wiki_controller_test.rb | 2 +- test/functional/wikis_controller_test.rb | 2 +- test/unit/activity_test.rb | 2 +- test/unit/changeset_test.rb | 2 +- test/unit/issue_test.rb | 6 +- test/unit/mail_handler_test.rb | 1 + test/unit/mailer_test.rb | 2 +- test/unit/member_test.rb | 38 +++++++++--- test/unit/message_test.rb | 6 +- test/unit/news_test.rb | 2 +- test/unit/project_test.rb | 4 +- test/unit/query_test.rb | 2 +- test/unit/search_test.rb | 3 +- test/unit/user_test.rb | 12 ++-- 65 files changed, 340 insertions(+), 175 deletions(-) create mode 100644 app/models/member_role.rb create mode 100644 db/migrate/20090503121501_create_member_roles.rb create mode 100644 db/migrate/20090503121505_populate_member_roles.rb create mode 100644 db/migrate/20090503121510_drop_members_role_id.rb create mode 100644 test/fixtures/member_roles.yml diff --git a/app/controllers/application.rb b/app/controllers/application.rb index f86487305..9123cfc07 100644 --- a/app/controllers/application.rb +++ b/app/controllers/application.rb @@ -38,10 +38,6 @@ class ApplicationController < ActionController::Base require_dependency "repository/#{scm.underscore}" end - def current_role - @current_role ||= User.current.role_for_project(@project) - end - def user_setup # Check the settings cache for each request Setting.check_cache diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 784d620e3..24c204098 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -146,7 +146,7 @@ class IssuesController < ApplicationController return end @issue.status = default_status - @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.role_for_project(@project), @issue.tracker)).uniq + @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq if request.get? || request.xhr? @issue.start_date ||= Date.today diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index e2bc9257f..eb5989525 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -48,7 +48,12 @@ class MembersController < ApplicationController if request.post? and @member.update_attributes(params[:member]) respond_to do |format| format.html { redirect_to :controller => 'projects', :action => 'settings', :tab => 'members', :id => @project } - format.js { render(:update) {|page| page.replace_html "tab-content-members", :partial => 'projects/settings/members'} } + format.js { + render(:update) {|page| + page.replace_html "tab-content-members", :partial => 'projects/settings/members' + page.visual_effect(:highlight, "member-#{@member.id}") + } + } end end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index b663291de..3f29e4e4c 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -112,7 +112,7 @@ class ProjectsController < ApplicationController redirect_to_project_menu_item(@project, params[:jump]) && return end - @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role} + @members_by_role = @project.members.find(:all, :include => [:user, :roles], :order => 'position').group_by {|m| m.roles.first} @subprojects = @project.children.visible @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC") @trackers = @project.rolled_up_trackers diff --git a/app/controllers/queries_controller.rb b/app/controllers/queries_controller.rb index b688d2c43..16755a125 100644 --- a/app/controllers/queries_controller.rb +++ b/app/controllers/queries_controller.rb @@ -24,7 +24,7 @@ class QueriesController < ApplicationController @query = Query.new(params[:query]) @query.project = params[:query_is_for_all] ? nil : @project @query.user = User.current - @query.is_public = false unless (@query.project && current_role.allowed_to?(:manage_public_queries)) || User.current.admin? + @query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin? @query.column_names = nil if params[:default_columns] params[:fields].each do |field| @@ -48,7 +48,7 @@ class QueriesController < ApplicationController end if params[:fields] @query.attributes = params[:query] @query.project = nil if params[:query_is_for_all] - @query.is_public = false unless (@query.project && current_role.allowed_to?(:manage_public_queries)) || User.current.admin? + @query.is_public = false unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin? @query.column_names = nil if params[:default_columns] if @query.save diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb index 6185d70ef..8074b18b9 100644 --- a/app/controllers/roles_controller.rb +++ b/app/controllers/roles_controller.rb @@ -40,7 +40,7 @@ class RolesController < ApplicationController @role.workflows.copy(copy_from) end flash[:notice] = l(:notice_successful_create) - redirect_to :action => 'list' + redirect_to :action => 'index' end @permissions = @role.setable_permissions @roles = Role.find :all, :order => 'builtin, position' @@ -50,7 +50,7 @@ class RolesController < ApplicationController @role = Role.find(params[:id]) if request.post? and @role.update_attributes(params[:role]) flash[:notice] = l(:notice_successful_update) - redirect_to :action => 'list' + redirect_to :action => 'index' end @permissions = @role.setable_permissions end @@ -58,7 +58,7 @@ class RolesController < ApplicationController def destroy @role = Role.find(params[:id]) @role.destroy - redirect_to :action => 'list' + redirect_to :action => 'index' rescue flash[:error] = 'This role is in use and can not be deleted.' redirect_to :action => 'index' @@ -73,7 +73,7 @@ class RolesController < ApplicationController role.save end flash[:notice] = l(:notice_successful_update) - redirect_to :action => 'list' + redirect_to :action => 'index' end end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index e0b508c7b..72a4b6c83 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -86,10 +86,7 @@ class UsersController < ApplicationController end end @auth_sources = AuthSource.find(:all) - @roles = Role.find_all_givable - @projects = Project.active.find(:all, :order => 'lft') @membership ||= Member.new - @memberships = @user.memberships end def edit_membership @@ -97,12 +94,23 @@ class UsersController < ApplicationController @membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:user => @user) @membership.attributes = params[:membership] @membership.save if request.post? - redirect_to :action => 'edit', :id => @user, :tab => 'memberships' + respond_to do |format| + format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } + format.js { + render(:update) {|page| + page.replace_html "tab-content-memberships", :partial => 'users/memberships' + page.visual_effect(:highlight, "member-#{@membership.id}") + } + } + end end def destroy_membership @user = User.find(params[:id]) Member.find(params[:membership_id]).destroy if request.post? - redirect_to :action => 'edit', :id => @user, :tab => 'memberships' + respond_to do |format| + format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } + format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} } + end end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index b7865ecde..2382983ff 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -28,10 +28,6 @@ module ApplicationHelper extend Forwardable def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter - def current_role - @current_role ||= User.current.role_for_project(@project) - end - # Return true if user is authorized for controller/action, otherwise false def authorize_for(controller, action) User.current.allowed_to?({:controller => controller, :action => action}, @project) diff --git a/app/models/issue.rb b/app/models/issue.rb index 23035b927..da49577e5 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -205,7 +205,7 @@ class Issue < ActiveRecord::Base # Returns an array of status that user is able to apply def new_statuses_allowed_to(user) - statuses = status.find_new_statuses_allowed_to(user.role_for_project(project), tracker) + statuses = status.find_new_statuses_allowed_to(user.roles_for_project(project), tracker) statuses << status unless statuses.empty? statuses.uniq.sort end diff --git a/app/models/issue_status.rb b/app/models/issue_status.rb index 16c7bce91..ca33d37d6 100644 --- a/app/models/issue_status.rb +++ b/app/models/issue_status.rb @@ -36,24 +36,34 @@ class IssueStatus < ActiveRecord::Base # Returns an array of all statuses the given role can switch to # Uses association cache when called more than one time - def new_statuses_allowed_to(role, tracker) - new_statuses = workflows.select {|w| w.role_id == role.id && w.tracker_id == tracker.id}.collect{|w| w.new_status} if role && tracker - new_statuses ? new_statuses.compact.sort{|x, y| x.position <=> y.position } : [] + def new_statuses_allowed_to(roles, tracker) + if roles && tracker + role_ids = roles.collect(&:id) + new_statuses = workflows.select {|w| role_ids.include?(w.role_id) && w.tracker_id == tracker.id}.collect{|w| w.new_status}.compact.sort + else + [] + end end # Same thing as above but uses a database query # More efficient than the previous method if called just once - def find_new_statuses_allowed_to(role, tracker) - new_statuses = workflows.find(:all, - :include => :new_status, - :conditions => ["role_id=? and tracker_id=?", role.id, tracker.id]).collect{ |w| w.new_status }.compact if role && tracker - new_statuses ? new_statuses.sort{|x, y| x.position <=> y.position } : [] + def find_new_statuses_allowed_to(roles, tracker) + if roles && tracker + workflows.find(:all, + :include => :new_status, + :conditions => { :role_id => roles.collect(&:id), + :tracker_id => tracker.id}).collect{ |w| w.new_status }.compact.sort + else + [] + end end - def new_status_allowed_to?(status, role, tracker) - status && role && tracker ? - !workflows.find(:first, :conditions => {:new_status_id => status.id, :role_id => role.id, :tracker_id => tracker.id}).nil? : + def new_status_allowed_to?(status, roles, tracker) + if status && roles && tracker + !workflows.find(:first, :conditions => {:new_status_id => status.id, :role_id => roles.collect(&:id), :tracker_id => tracker.id}).nil? + else false + end end def <=>(status) diff --git a/app/models/member.rb b/app/models/member.rb index 5228d1ffc..2dc91cba7 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -1,5 +1,5 @@ -# redMine - project management software -# Copyright (C) 2006 Jean-Philippe Lang +# Redmine - project management software +# Copyright (C) 2006-2009 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,15 +17,12 @@ class Member < ActiveRecord::Base belongs_to :user - belongs_to :role + has_many :member_roles, :dependent => :delete_all + has_many :roles, :through => :member_roles belongs_to :project - validates_presence_of :role, :user, :project + validates_presence_of :user, :project validates_uniqueness_of :user_id, :scope => :project_id - - def validate - errors.add :role_id, :invalid if role && !role.member? - end def name self.user.name @@ -42,11 +39,18 @@ class Member < ActiveRecord::Base end def <=>(member) - role == member.role ? (user <=> member.user) : (role <=> member.role) + a, b = roles.sort.first, member.roles.sort.first + a == b ? (user <=> member.user) : (a <=> b) end def before_destroy # remove category based auto assignments for this member IssueCategory.update_all "assigned_to_id = NULL", ["project_id = ? AND assigned_to_id = ?", project.id, user.id] end + + protected + + def validate + errors.add_to_base "Role can't be blank" if roles.empty? + end end diff --git a/app/models/member_role.rb b/app/models/member_role.rb new file mode 100644 index 000000000..46777cd1e --- /dev/null +++ b/app/models/member_role.rb @@ -0,0 +1,27 @@ +# Redmine - project management software +# Copyright (C) 2006-2009 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +class MemberRole < ActiveRecord::Base + belongs_to :member + belongs_to :role + + validates_presence_of :role + + def validate + errors.add :role_id, :invalid if role && !role.member? + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 261844e8e..922f522f1 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -135,7 +135,7 @@ class Project < ActiveRecord::Base statements << "1=0" if user.logged? statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" if Role.non_member.allowed_to?(permission) - allowed_project_ids = user.memberships.select {|m| m.role.allowed_to?(permission)}.collect {|m| m.project_id} + allowed_project_ids = user.memberships.select {|m| m.roles.detect {|role| role.allowed_to?(permission)}}.collect {|m| m.project_id} statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any? elsif Role.anonymous.allowed_to?(permission) # anonymous user allowed on public project @@ -247,12 +247,14 @@ class Project < ActiveRecord::Base # Deletes all project's members def delete_all_members + me, mr = Member.table_name, MemberRole.table_name + connection.delete("DELETE FROM #{mr} WHERE #{mr}.member_id IN (SELECT #{me}.id FROM #{me} WHERE #{me}.project_id = #{id})") Member.delete_all(['project_id = ?', id]) end # Users issues can be assigned to def assignable_users - members.select {|m| m.role.assignable?}.collect {|m| m.user}.sort + members.select {|m| m.roles.detect {|role| role.assignable?}}.collect {|m| m.user}.sort end # Returns the mail adresses of users that should be always notified on project events diff --git a/app/models/role.rb b/app/models/role.rb index b07e7a039..a93c4eaca 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -38,7 +38,8 @@ class Role < ActiveRecord::Base end end - has_many :members + has_many :member_roles, :dependent => :destroy + has_many :members, :through => :member_roles acts_as_list serialize :permissions, Array @@ -82,7 +83,11 @@ class Role < ActiveRecord::Base end def <=>(role) - position <=> role.position + role ? position <=> role.position : -1 + end + + def to_s + name end # Return true if the role is a builtin role diff --git a/app/models/user.rb b/app/models/user.rb index 3c9a1c753..3c6f72387 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -33,7 +33,7 @@ class User < ActiveRecord::Base :username => '#{login}' } - has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name" + has_many :memberships, :class_name => 'Member', :include => [ :project, :roles ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name" has_many :members, :dependent => :delete_all has_many :projects, :through => :memberships has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify @@ -229,26 +229,30 @@ class User < ActiveRecord::Base !logged? end - # Return user's role for project - def role_for_project(project) + # Return user's roles for project + def roles_for_project(project) + roles = [] # No role on archived projects - return nil unless project && project.active? + return roles unless project && project.active? if logged? # Find project membership membership = memberships.detect {|m| m.project_id == project.id} if membership - membership.role + roles = membership.roles else @role_non_member ||= Role.non_member + roles << @role_non_member end else @role_anonymous ||= Role.anonymous + roles << @role_anonymous end + roles end # Return true if the user is a member of project def member_of?(project) - role_for_project(project).member? + !roles_for_project(project).detect {|role| role.member?}.nil? end # Return true if the user is allowed to do the specified action on project @@ -264,13 +268,13 @@ class User < ActiveRecord::Base # Admin users are authorized for anything else return true if admin? - role = role_for_project(project) - return false unless role - role.allowed_to?(action) && (project.is_public? || role.member?) + roles = roles_for_project(project) + return false unless roles + roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)} elsif options[:global] # authorize if user has at least one role that has this permission - roles = memberships.collect {|m| m.role}.uniq + roles = memberships.collect {|m| m.roles}.flatten.uniq roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action)) else false diff --git a/app/views/account/show.rhtml b/app/views/account/show.rhtml index 6b70c5f86..c97da7f97 100644 --- a/app/views/account/show.rhtml +++ b/app/views/account/show.rhtml @@ -25,7 +25,7 @@ <% end %> diff --git a/app/views/projects/settings/_members.rhtml b/app/views/projects/settings/_members.rhtml index 312403993..a6bdd9504 100644 --- a/app/views/projects/settings/_members.rhtml +++ b/app/views/projects/settings/_members.rhtml @@ -1,36 +1,43 @@ <%= error_messages_for 'member' %> <% roles = Role.find_all_givable - members = @project.members.find(:all, :include => [:role, :user]).sort %> + members = @project.members.find(:all, :include => [:roles, :user]).sort %>
<% if members.any? %> - +
- + <%= call_hook(:view_projects_settings_members_table_header, :project => @project) %> <% members.each do |member| %> <% next if member.new_record? %> - - - + + - + - <%= call_hook(:view_projects_settings_members_table_row, { :project => @project, :member => member}) %> + + <%= call_hook(:view_projects_settings_members_table_row, { :project => @project, :member => member}) %> <% end; reset_cycle %> @@ -58,8 +65,11 @@ <% end -%> <% end %> -

<%= l(:label_role) %>: <%= f.select :role_id, roles.collect{|role| [role.name, role.id]}, :selected => nil %> - <%= submit_tag l(:button_add) %>

+

<%= l(:label_role_plural) %>: + <% roles.each do |role| %> + + <% end %>

+

<%= submit_tag l(:button_add) %>

<% end %> <% end %> diff --git a/app/views/queries/_form.rhtml b/app/views/queries/_form.rhtml index 28faba177..577699d70 100644 --- a/app/views/queries/_form.rhtml +++ b/app/views/queries/_form.rhtml @@ -6,7 +6,7 @@

<%= text_field 'query', 'name', :size => 80 %>

-<% if User.current.admin? || (@project && current_role.allowed_to?(:manage_public_queries)) %> +<% if User.current.admin? || User.current.allowed_to?(:manage_public_queries, @project) %>

<%= check_box 'query', 'is_public', :onchange => (User.current.admin? ? nil : 'if (this.checked) {$("query_is_for_all").checked = false; $("query_is_for_all").disabled = true;} else {$("query_is_for_all").disabled = false;}') %>

diff --git a/app/views/users/_memberships.rhtml b/app/views/users/_memberships.rhtml index d1657fb98..8b458e0b2 100644 --- a/app/views/users/_memberships.rhtml +++ b/app/views/users/_memberships.rhtml @@ -1,24 +1,36 @@ -<% if @memberships.any? %> +<% roles = Role.find_all_givable %> +<% projects = Project.active.find(:all, :order => 'lft') %> + +
+<% if @user.memberships.any? %>
<%= l(:label_user) %><%= l(:label_role) %><%= l(:label_role_plural) %>
<%=h member.name %> +
<%= link_to_user member.user %> + <%=h member.roles.sort.collect(&:to_s).join(', ') %> <% if authorize_for('members', 'edit') %> - <% remote_form_for(:member, member, :url => {:controller => 'members', :action => 'edit', :id => member}, :method => :post) do |f| %> - <%= f.select :role_id, roles.collect{|role| [role.name, role.id]}, {}, :class => "small" %> - <%= submit_tag l(:button_change), :class => "small" %> + <% remote_form_for(:member, member, :url => {:controller => 'members', :action => 'edit', :id => member}, + :method => :post, + :html => { :id => "member-#{member.id}-roles-form", :style => 'display:none;' }) do |f| %> +

<% roles.each do |role| %> +
+ <% end %>

+

<%= submit_tag l(:button_change), :class => "small" %> + <%= link_to_function l(:button_cancel), "$('member-#{member.id}-roles').show(); $('member-#{member.id}-roles-form').hide(); return false;" %>

<% end %> <% end %> -
+ + <%= link_to_function l(:button_edit), "$('member-#{member.id}-roles').hide(); $('member-#{member.id}-roles-form').show(); return false;", :class => 'icon icon-edit' %> <%= link_to_remote l(:button_delete), { :url => {:controller => 'members', :action => 'destroy', :id => member}, :method => :post }, :title => l(:button_delete), :class => 'icon icon-del' %> -
- + - <% @memberships.each do |membership| %> + <% @user.memberships.each do |membership| %> <% next if membership.new_record? %> - - - + + - + + <% end; reset_cycle %> @@ -26,15 +38,19 @@ <% else %>

<%= l(:label_no_data) %>

<% end %> + -<% if @projects.any? %> -

-
-<% form_tag({ :action => 'edit_membership', :id => @user }) do %> -<%= select_tag 'membership[project_id]', options_for_membership_project_select(@user, @projects) %> -<%= l(:label_role) %>: -<%= select_tag 'membership[role_id]', options_from_collection_for_select(@roles, "id", "name") %> -<%= submit_tag l(:button_add) %> +

+<% if projects.any? %> +
<%=l(:label_project_new)%> +<% remote_form_for(:membership, :url => { :action => 'edit_membership', :id => @user }) do %> +<%= select_tag 'membership[project_id]', options_for_membership_project_select(@user, projects) %> +

<%= l(:label_role_plural) %>: +<% roles.each do |role| %> + +<% end %>

+

<%= submit_tag l(:button_add) %>

<% end %> -

+
<% end %> +
diff --git a/app/views/workflows/edit.rhtml b/app/views/workflows/edit.rhtml index 1aef8eb7e..134dfb3e7 100644 --- a/app/views/workflows/edit.rhtml +++ b/app/views/workflows/edit.rhtml @@ -43,7 +43,7 @@ <% for old_status in @statuses %> "> - <% new_status_ids_allowed = old_status.find_new_statuses_allowed_to(@role, @tracker).collect(&:id) -%> + <% new_status_ids_allowed = old_status.find_new_statuses_allowed_to([@role], @tracker).collect(&:id) -%> <% for new_status in @statuses -%>
<%= l(:label_project) %><%= l(:label_role) %><%= l(:label_role_plural) %>
<%=h membership.project %> - <% form_tag({ :action => 'edit_membership', :id => @user, :membership_id => membership }) do %> - <%= select_tag 'membership[role_id]', options_from_collection_for_select(@roles, "id", "name", membership.role_id) %> - <%= submit_tag l(:button_change), :class => "small" %> +
<%=h membership.project %> + <%=h membership.roles.sort.collect(&:to_s).join(', ') %> + <% remote_form_for(:membership, :url => { :action => 'edit_membership', :id => @user, :membership_id => membership }, + :html => { :id => "member-#{membership.id}-roles-form", :style => 'display:none;'}) do %> +

<% roles.each do |role| %> +
+ <% end %>

+

<%= submit_tag l(:button_change) %> + <%= link_to_function l(:button_cancel), "$('member-#{membership.id}-roles').show(); $('member-#{membership.id}-roles-form').hide(); return false;" %>

<% end %> -
- <%= link_to l(:button_delete), {:action => 'destroy_membership', :id => @user, :membership_id => membership }, :method => :post, :class => 'icon icon-del' %> - + <%= link_to_function l(:button_edit), "$('member-#{membership.id}-roles').hide(); $('member-#{membership.id}-roles-form').show(); return false;", :class => 'icon icon-edit' %> + <%= link_to_remote l(:button_delete), { :url => { :controller => 'users', :action => 'destroy_membership', :id => @user, :membership_id => membership }, + :method => :post }, + :class => 'icon icon-del' %> +
<%= old_status.name %> false + t.column :role_id, :integer, :null => false + end + end + + def self.down + drop_table :member_roles + end +end diff --git a/db/migrate/20090503121505_populate_member_roles.rb b/db/migrate/20090503121505_populate_member_roles.rb new file mode 100644 index 000000000..8b7c98750 --- /dev/null +++ b/db/migrate/20090503121505_populate_member_roles.rb @@ -0,0 +1,11 @@ +class PopulateMemberRoles < ActiveRecord::Migration + def self.up + Member.find(:all).each do |member| + MemberRole.create!(:member_id => member.id, :role_id => member.role_id) + end + end + + def self.down + MemberRole.delete_all + end +end diff --git a/db/migrate/20090503121510_drop_members_role_id.rb b/db/migrate/20090503121510_drop_members_role_id.rb new file mode 100644 index 000000000..c28119910 --- /dev/null +++ b/db/migrate/20090503121510_drop_members_role_id.rb @@ -0,0 +1,9 @@ +class DropMembersRoleId < ActiveRecord::Migration + def self.up + remove_column :members, :role_id + end + + def self.down + raise IrreversibleMigration + end +end diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index b3db22619..813c8f194 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -87,6 +87,8 @@ table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; } table.list td { vertical-align: top; } table.list td.id { width: 2%; text-align: center;} table.list td.checkbox { width: 15px; padding: 0px;} +table.list td.buttons { width: 15%; white-space:nowrap; text-align: right; } +table.list td.buttons a { padding-right: 0.6em; } tr.project td.name a { padding-left: 16px; white-space:nowrap; } tr.project.parent td.name a { background: url('../images/bullet_toggle_minus.png') no-repeat; } @@ -114,6 +116,8 @@ table.files tr.file td { text-align: center; } table.files tr.file td.filename { text-align: left; padding-left: 24px; } table.files tr.file td.digest { font-size: 80%; } +table.members td.roles, table.memberships td.roles { width: 45%; } + tr.message { height: 2.6em; } tr.message td.last_message { font-size: 80%; } tr.message.locked td.subject a { background-image: url(../images/locked.png); } @@ -333,11 +337,11 @@ p.other-formats { text-align: right; font-size:0.9em; color: #666; } a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; } /* Project members tab */ -div#tab-content-members .splitcontentleft { width: 64% } -div#tab-content-members .splitcontentright { width: 34% } -div#tab-content-members fieldset { padding:1em; margin-bottom: 1em; } -div#tab-content-members fieldset legend { font-weight: bold; } -div#tab-content-members fieldset label { display: block; } +div#tab-content-members .splitcontentleft, div#tab-content-memberships .splitcontentleft { width: 64% } +div#tab-content-members .splitcontentright, div#tab-content-memberships .splitcontentright { width: 34% } +div#tab-content-members fieldset, div#tab-content-memberships fieldset { padding:1em; margin-bottom: 1em; } +div#tab-content-members fieldset legend, div#tab-content-memberships fieldset legend { font-weight: bold; } +div#tab-content-members fieldset label, div#tab-content-memberships fieldset label { display: block; } div#tab-content-members fieldset div { max-height: 400px; overflow:auto; } * html div#tab-content-members fieldset div { height: 450px; } diff --git a/test/fixtures/member_roles.yml b/test/fixtures/member_roles.yml new file mode 100644 index 000000000..6c91f15a4 --- /dev/null +++ b/test/fixtures/member_roles.yml @@ -0,0 +1,23 @@ +--- +member_roles_001: + id: 1 + role_id: 1 + member_id: 1 +member_roles_002: + id: 2 + role_id: 2 + member_id: 2 +member_roles_003: + id: 3 + role_id: 2 + member_id: 3 +member_roles_004: + id: 4 + role_id: 2 + member_id: 4 +member_roles_005: + id: 5 + role_id: 1 + member_id: 5 + + \ No newline at end of file diff --git a/test/fixtures/members.yml b/test/fixtures/members.yml index 4156bb867..b1b19c3f5 100644 --- a/test/fixtures/members.yml +++ b/test/fixtures/members.yml @@ -2,21 +2,18 @@ members_001: created_on: 2006-07-19 19:35:33 +02:00 project_id: 1 - role_id: 1 id: 1 user_id: 2 mail_notification: true members_002: created_on: 2006-07-19 19:35:36 +02:00 project_id: 1 - role_id: 2 id: 2 user_id: 3 mail_notification: true members_003: created_on: 2006-07-19 19:35:36 +02:00 project_id: 2 - role_id: 2 id: 3 user_id: 2 mail_notification: true @@ -24,7 +21,6 @@ members_004: id: 4 created_on: 2006-07-19 19:35:36 +02:00 project_id: 1 - role_id: 2 # Locked user user_id: 5 mail_notification: true @@ -32,7 +28,6 @@ members_005: id: 5 created_on: 2006-07-19 19:35:33 +02:00 project_id: 5 - role_id: 1 user_id: 2 mail_notification: true \ No newline at end of file diff --git a/test/functional/attachments_controller_test.rb b/test/functional/attachments_controller_test.rb index a49caa876..3a4b89785 100644 --- a/test/functional/attachments_controller_test.rb +++ b/test/functional/attachments_controller_test.rb @@ -23,7 +23,7 @@ class AttachmentsController; def rescue_action(e) raise e end; end class AttachmentsControllerTest < Test::Unit::TestCase - fixtures :users, :projects, :roles, :members, :enabled_modules, :issues, :trackers, :attachments, + fixtures :users, :projects, :roles, :members, :member_roles, :enabled_modules, :issues, :trackers, :attachments, :versions, :wiki_pages, :wikis, :documents def setup diff --git a/test/functional/boards_controller_test.rb b/test/functional/boards_controller_test.rb index 01db0f94b..eb9a50ea5 100644 --- a/test/functional/boards_controller_test.rb +++ b/test/functional/boards_controller_test.rb @@ -22,7 +22,7 @@ require 'boards_controller' class BoardsController; def rescue_action(e) raise e end; end class BoardsControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :members, :roles, :boards, :messages, :enabled_modules + fixtures :projects, :users, :members, :member_roles, :roles, :boards, :messages, :enabled_modules def setup @controller = BoardsController.new diff --git a/test/functional/documents_controller_test.rb b/test/functional/documents_controller_test.rb index b5788c776..c0fe0957e 100644 --- a/test/functional/documents_controller_test.rb +++ b/test/functional/documents_controller_test.rb @@ -22,7 +22,7 @@ require 'documents_controller' class DocumentsController; def rescue_action(e) raise e end; end class DocumentsControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :enabled_modules, :documents, :enumerations + fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :documents, :enumerations def setup @controller = DocumentsController.new diff --git a/test/functional/issue_categories_controller_test.rb b/test/functional/issue_categories_controller_test.rb index 5bd826370..ffb87339d 100644 --- a/test/functional/issue_categories_controller_test.rb +++ b/test/functional/issue_categories_controller_test.rb @@ -22,7 +22,7 @@ require 'issue_categories_controller' class IssueCategoriesController; def rescue_action(e) raise e end; end class IssueCategoriesControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :members, :roles, :enabled_modules, :issue_categories + fixtures :projects, :users, :members, :member_roles, :roles, :enabled_modules, :issue_categories def setup @controller = IssueCategoriesController.new diff --git a/test/functional/issue_relations_controller_test.rb b/test/functional/issue_relations_controller_test.rb index dc64a004e..a23f64a96 100644 --- a/test/functional/issue_relations_controller_test.rb +++ b/test/functional/issue_relations_controller_test.rb @@ -10,6 +10,7 @@ class IssueRelationsControllerTest < Test::Unit::TestCase :users, :roles, :members, + :member_roles, :issues, :issue_statuses, :enabled_modules, diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index dfea32899..2bb95f8bf 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -26,6 +26,7 @@ class IssuesControllerTest < Test::Unit::TestCase :users, :roles, :members, + :member_roles, :issues, :issue_statuses, :versions, diff --git a/test/functional/journals_controller_test.rb b/test/functional/journals_controller_test.rb index 327c7e79a..ae112ab6f 100644 --- a/test/functional/journals_controller_test.rb +++ b/test/functional/journals_controller_test.rb @@ -22,7 +22,7 @@ require 'journals_controller' class JournalsController; def rescue_action(e) raise e end; end class JournalsControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :members, :roles, :issues, :journals, :journal_details, :enabled_modules + fixtures :projects, :users, :members, :member_roles, :roles, :issues, :journals, :journal_details, :enabled_modules def setup @controller = JournalsController.new diff --git a/test/functional/mail_handler_controller_test.rb b/test/functional/mail_handler_controller_test.rb index 6c5af23f0..e99f99a2c 100644 --- a/test/functional/mail_handler_controller_test.rb +++ b/test/functional/mail_handler_controller_test.rb @@ -22,7 +22,7 @@ require 'mail_handler_controller' class MailHandlerController; def rescue_action(e) raise e end; end class MailHandlerControllerTest < Test::Unit::TestCase - fixtures :users, :projects, :enabled_modules, :roles, :members, :issues, :issue_statuses, :trackers, :enumerations + fixtures :users, :projects, :enabled_modules, :roles, :members, :member_roles, :issues, :issue_statuses, :trackers, :enumerations FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' diff --git a/test/functional/members_controller_test.rb b/test/functional/members_controller_test.rb index efd28a414..2680c3934 100644 --- a/test/functional/members_controller_test.rb +++ b/test/functional/members_controller_test.rb @@ -23,7 +23,7 @@ class MembersController; def rescue_action(e) raise e end; end class MembersControllerTest < Test::Unit::TestCase - fixtures :projects, :members, :roles, :users + fixtures :projects, :members, :member_roles, :roles, :users def setup @controller = MembersController.new @@ -42,7 +42,7 @@ class MembersControllerTest < Test::Unit::TestCase def test_create assert_difference 'Member.count' do - post :new, :id => 1, :member => {:role_id => 1, :user_id => 7} + post :new, :id => 1, :member => {:role_ids => [1], :user_id => 7} end assert_redirected_to '/projects/ecookbook/settings/members' assert User.find(7).member_of?(Project.find(1)) @@ -50,7 +50,7 @@ class MembersControllerTest < Test::Unit::TestCase def test_create_by_user_login assert_difference 'Member.count' do - post :new, :id => 1, :member => {:role_id => 1, :user_login => 'someone'} + post :new, :id => 1, :member => {:role_ids => [1], :user_login => 'someone'} end assert_redirected_to '/projects/ecookbook/settings/members' assert User.find(7).member_of?(Project.find(1)) @@ -58,7 +58,7 @@ class MembersControllerTest < Test::Unit::TestCase def test_create_multiple assert_difference 'Member.count', 3 do - post :new, :id => 1, :member => {:role_id => 1, :user_ids => [7, 8, 9]} + post :new, :id => 1, :member => {:role_ids => [1], :user_ids => [7, 8, 9]} end assert_redirected_to '/projects/ecookbook/settings/members' assert User.find(7).member_of?(Project.find(1)) @@ -66,7 +66,7 @@ class MembersControllerTest < Test::Unit::TestCase def test_edit assert_no_difference 'Member.count' do - post :edit, :id => 2, :member => {:role_id => 1, :user_id => 3} + post :edit, :id => 2, :member => {:role_ids => [1], :user_id => 3} end assert_redirected_to '/projects/ecookbook/settings/members' end diff --git a/test/functional/messages_controller_test.rb b/test/functional/messages_controller_test.rb index 94062b402..70061aca3 100644 --- a/test/functional/messages_controller_test.rb +++ b/test/functional/messages_controller_test.rb @@ -22,7 +22,7 @@ require 'messages_controller' class MessagesController; def rescue_action(e) raise e end; end class MessagesControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :members, :roles, :boards, :messages, :enabled_modules + fixtures :projects, :users, :members, :member_roles, :roles, :boards, :messages, :enabled_modules def setup @controller = MessagesController.new diff --git a/test/functional/news_controller_test.rb b/test/functional/news_controller_test.rb index 22ad2d241..a31362444 100644 --- a/test/functional/news_controller_test.rb +++ b/test/functional/news_controller_test.rb @@ -22,7 +22,7 @@ require 'news_controller' class NewsController; def rescue_action(e) raise e end; end class NewsControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :enabled_modules, :news, :comments + fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :news, :comments def setup @controller = NewsController.new diff --git a/test/functional/projects_controller_test.rb b/test/functional/projects_controller_test.rb index 4393ac075..2fba106e3 100644 --- a/test/functional/projects_controller_test.rb +++ b/test/functional/projects_controller_test.rb @@ -22,7 +22,7 @@ require 'projects_controller' class ProjectsController; def rescue_action(e) raise e end; end class ProjectsControllerTest < Test::Unit::TestCase - fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details, + fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details, :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages, :attachments diff --git a/test/functional/queries_controller_test.rb b/test/functional/queries_controller_test.rb index 1951aff8f..8edde44aa 100644 --- a/test/functional/queries_controller_test.rb +++ b/test/functional/queries_controller_test.rb @@ -22,7 +22,7 @@ require 'queries_controller' class QueriesController; def rescue_action(e) raise e end; end class QueriesControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :members, :roles, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :queries + fixtures :projects, :users, :members, :member_roles, :roles, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :queries def setup @controller = QueriesController.new diff --git a/test/functional/repositories_bazaar_controller_test.rb b/test/functional/repositories_bazaar_controller_test.rb index acb6c1d21..b1787a538 100644 --- a/test/functional/repositories_bazaar_controller_test.rb +++ b/test/functional/repositories_bazaar_controller_test.rb @@ -22,7 +22,7 @@ require 'repositories_controller' class RepositoriesController; def rescue_action(e) raise e end; end class RepositoriesBazaarControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :repositories, :enabled_modules + fixtures :projects, :users, :roles, :members, :member_roles, :repositories, :enabled_modules # No '..' in the repository path REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/bazaar_repository' diff --git a/test/functional/repositories_controller_test.rb b/test/functional/repositories_controller_test.rb index ceb7341b0..d51a42976 100644 --- a/test/functional/repositories_controller_test.rb +++ b/test/functional/repositories_controller_test.rb @@ -22,7 +22,7 @@ require 'repositories_controller' class RepositoriesController; def rescue_action(e) raise e end; end class RepositoriesControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :repositories, :issues, :issue_statuses, :changesets, :changes, :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers + fixtures :projects, :users, :roles, :members, :member_roles, :repositories, :issues, :issue_statuses, :changesets, :changes, :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers def setup @controller = RepositoriesController.new diff --git a/test/functional/repositories_darcs_controller_test.rb b/test/functional/repositories_darcs_controller_test.rb index 43c715924..8f1c7df98 100644 --- a/test/functional/repositories_darcs_controller_test.rb +++ b/test/functional/repositories_darcs_controller_test.rb @@ -22,7 +22,7 @@ require 'repositories_controller' class RepositoriesController; def rescue_action(e) raise e end; end class RepositoriesDarcsControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :repositories, :enabled_modules + fixtures :projects, :users, :roles, :members, :member_roles, :repositories, :enabled_modules # No '..' in the repository path REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/darcs_repository' diff --git a/test/functional/repositories_git_controller_test.rb b/test/functional/repositories_git_controller_test.rb index 201a50677..7f63ea3a9 100644 --- a/test/functional/repositories_git_controller_test.rb +++ b/test/functional/repositories_git_controller_test.rb @@ -22,7 +22,7 @@ require 'repositories_controller' class RepositoriesController; def rescue_action(e) raise e end; end class RepositoriesGitControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :repositories, :enabled_modules + fixtures :projects, :users, :roles, :members, :member_roles, :repositories, :enabled_modules # No '..' in the repository path REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository' diff --git a/test/functional/repositories_mercurial_controller_test.rb b/test/functional/repositories_mercurial_controller_test.rb index cb870aa32..53cbedd00 100644 --- a/test/functional/repositories_mercurial_controller_test.rb +++ b/test/functional/repositories_mercurial_controller_test.rb @@ -22,7 +22,7 @@ require 'repositories_controller' class RepositoriesController; def rescue_action(e) raise e end; end class RepositoriesMercurialControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :repositories, :enabled_modules + fixtures :projects, :users, :roles, :members, :member_roles, :repositories, :enabled_modules # No '..' in the repository path REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository' diff --git a/test/functional/repositories_subversion_controller_test.rb b/test/functional/repositories_subversion_controller_test.rb index aef8c2ef2..e31094e7b 100644 --- a/test/functional/repositories_subversion_controller_test.rb +++ b/test/functional/repositories_subversion_controller_test.rb @@ -22,7 +22,7 @@ require 'repositories_controller' class RepositoriesController; def rescue_action(e) raise e end; end class RepositoriesSubversionControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :enabled_modules, + fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :repositories, :issues, :issue_statuses, :changesets, :changes, :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers diff --git a/test/functional/roles_controller_test.rb b/test/functional/roles_controller_test.rb index 5c47be180..61e9bd6df 100644 --- a/test/functional/roles_controller_test.rb +++ b/test/functional/roles_controller_test.rb @@ -22,7 +22,7 @@ require 'roles_controller' class RolesController; def rescue_action(e) raise e end; end class RolesControllerTest < Test::Unit::TestCase - fixtures :roles, :users, :members, :workflows + fixtures :roles, :users, :members, :member_roles, :workflows def setup @controller = RolesController.new @@ -65,7 +65,7 @@ class RolesControllerTest < Test::Unit::TestCase :permissions => ['add_issues', 'edit_issues', 'log_time', ''], :assignable => '0'} - assert_redirected_to 'roles/list' + assert_redirected_to 'roles' role = Role.find_by_name('RoleWithoutWorkflowCopy') assert_not_nil role assert_equal [:add_issues, :edit_issues, :log_time], role.permissions @@ -78,7 +78,7 @@ class RolesControllerTest < Test::Unit::TestCase :assignable => '0'}, :copy_workflow_from => '1' - assert_redirected_to 'roles/list' + assert_redirected_to 'roles' role = Role.find_by_name('RoleWithWorkflowCopy') assert_not_nil role assert_equal Role.find(1).workflows.size, role.workflows.size @@ -97,7 +97,7 @@ class RolesControllerTest < Test::Unit::TestCase :permissions => ['edit_project', ''], :assignable => '0'} - assert_redirected_to 'roles/list' + assert_redirected_to 'roles' role = Role.find(1) assert_equal [:edit_project], role.permissions end @@ -107,7 +107,7 @@ class RolesControllerTest < Test::Unit::TestCase assert r.save post :destroy, :id => r - assert_redirected_to 'roles/list' + assert_redirected_to 'roles' assert_nil Role.find_by_id(r.id) end @@ -139,7 +139,7 @@ class RolesControllerTest < Test::Unit::TestCase def test_post_report post :report, :permissions => { '0' => '', '1' => ['edit_issues'], '3' => ['add_issues', 'delete_issues']} - assert_redirected_to 'roles/list' + assert_redirected_to 'roles' assert_equal [:edit_issues], Role.find(1).permissions assert_equal [:add_issues, :delete_issues], Role.find(3).permissions @@ -148,33 +148,33 @@ class RolesControllerTest < Test::Unit::TestCase def test_clear_all_permissions post :report, :permissions => { '0' => '' } - assert_redirected_to 'roles/list' + assert_redirected_to 'roles' assert Role.find(1).permissions.empty? end def test_move_highest post :edit, :id => 3, :role => {:move_to => 'highest'} - assert_redirected_to 'roles/list' + assert_redirected_to 'roles' assert_equal 1, Role.find(3).position end def test_move_higher position = Role.find(3).position post :edit, :id => 3, :role => {:move_to => 'higher'} - assert_redirected_to 'roles/list' + assert_redirected_to 'roles' assert_equal position - 1, Role.find(3).position end def test_move_lower position = Role.find(2).position post :edit, :id => 2, :role => {:move_to => 'lower'} - assert_redirected_to 'roles/list' + assert_redirected_to 'roles' assert_equal position + 1, Role.find(2).position end def test_move_lowest post :edit, :id => 2, :role => {:move_to => 'lowest'} - assert_redirected_to 'roles/list' + assert_redirected_to 'roles' assert_equal Role.count, Role.find(2).position end end diff --git a/test/functional/search_controller_test.rb b/test/functional/search_controller_test.rb index ec81e417f..0f74c537e 100644 --- a/test/functional/search_controller_test.rb +++ b/test/functional/search_controller_test.rb @@ -5,7 +5,7 @@ require 'search_controller' class SearchController; def rescue_action(e) raise e end; end class SearchControllerTest < Test::Unit::TestCase - fixtures :projects, :enabled_modules, :roles, :users, + fixtures :projects, :enabled_modules, :roles, :users, :members, :member_roles, :issues, :trackers, :issue_statuses, :custom_fields, :custom_values, :repositories, :changesets diff --git a/test/functional/timelog_controller_test.rb b/test/functional/timelog_controller_test.rb index a69a70ec0..df80f5d5a 100644 --- a/test/functional/timelog_controller_test.rb +++ b/test/functional/timelog_controller_test.rb @@ -22,7 +22,7 @@ require 'timelog_controller' class TimelogController; def rescue_action(e) raise e end; end class TimelogControllerTest < Test::Unit::TestCase - fixtures :projects, :enabled_modules, :roles, :members, :issues, :time_entries, :users, :trackers, :enumerations, :issue_statuses, :custom_fields, :custom_values + fixtures :projects, :enabled_modules, :roles, :members, :member_roles, :issues, :time_entries, :users, :trackers, :enumerations, :issue_statuses, :custom_fields, :custom_values def setup @controller = TimelogController.new diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb index d41f88214..e9aad1a98 100644 --- a/test/functional/users_controller_test.rb +++ b/test/functional/users_controller_test.rb @@ -24,7 +24,7 @@ class UsersController; def rescue_action(e) raise e end; end class UsersControllerTest < Test::Unit::TestCase include Redmine::I18n - fixtures :users, :projects, :members + fixtures :users, :projects, :members, :member_roles, :roles def setup @controller = UsersController.new @@ -123,9 +123,9 @@ class UsersControllerTest < Test::Unit::TestCase def test_edit_membership post :edit_membership, :id => 2, :membership_id => 1, - :membership => { :role_id => 2} + :membership => { :role_ids => [2]} assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships' - assert_equal 2, Member.find(1).role_id + assert_equal [2], Member.find(1).role_ids end def test_edit_with_activation_should_send_a_notification diff --git a/test/functional/versions_controller_test.rb b/test/functional/versions_controller_test.rb index 2ddf3a9f1..3d212def3 100644 --- a/test/functional/versions_controller_test.rb +++ b/test/functional/versions_controller_test.rb @@ -22,7 +22,7 @@ require 'versions_controller' class VersionsController; def rescue_action(e) raise e end; end class VersionsControllerTest < Test::Unit::TestCase - fixtures :projects, :versions, :issues, :users, :roles, :members, :enabled_modules + fixtures :projects, :versions, :issues, :users, :roles, :members, :member_roles, :enabled_modules def setup @controller = VersionsController.new diff --git a/test/functional/watchers_controller_test.rb b/test/functional/watchers_controller_test.rb index cd6539410..275599f9b 100644 --- a/test/functional/watchers_controller_test.rb +++ b/test/functional/watchers_controller_test.rb @@ -22,7 +22,7 @@ require 'watchers_controller' class WatchersController; def rescue_action(e) raise e end; end class WatchersControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :enabled_modules, + fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :issues, :trackers, :projects_trackers, :issue_statuses, :enumerations, :watchers def setup diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb index 22d816e59..9d104c527 100644 --- a/test/functional/wiki_controller_test.rb +++ b/test/functional/wiki_controller_test.rb @@ -22,7 +22,7 @@ require 'wiki_controller' class WikiController; def rescue_action(e) raise e end; end class WikiControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :enabled_modules, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, :attachments + fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, :attachments def setup @controller = WikiController.new diff --git a/test/functional/wikis_controller_test.rb b/test/functional/wikis_controller_test.rb index c558db0cb..7a5634f1d 100644 --- a/test/functional/wikis_controller_test.rb +++ b/test/functional/wikis_controller_test.rb @@ -22,7 +22,7 @@ require 'wikis_controller' class WikisController; def rescue_action(e) raise e end; end class WikisControllerTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :enabled_modules, :wikis + fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :wikis def setup @controller = WikisController.new diff --git a/test/unit/activity_test.rb b/test/unit/activity_test.rb index d47694e48..0b1773dd2 100644 --- a/test/unit/activity_test.rb +++ b/test/unit/activity_test.rb @@ -18,7 +18,7 @@ require File.dirname(__FILE__) + '/../test_helper' class ActivityTest < Test::Unit::TestCase - fixtures :projects, :versions, :attachments, :users, :roles, :members, :issues, :journals, :journal_details, + fixtures :projects, :versions, :attachments, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details, :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages def setup diff --git a/test/unit/changeset_test.rb b/test/unit/changeset_test.rb index 6a0df2c5d..78e2339f9 100644 --- a/test/unit/changeset_test.rb +++ b/test/unit/changeset_test.rb @@ -18,7 +18,7 @@ require File.dirname(__FILE__) + '/../test_helper' class ChangesetTest < Test::Unit::TestCase - fixtures :projects, :repositories, :issues, :issue_statuses, :changesets, :changes, :issue_categories, :enumerations, :custom_fields, :custom_values, :users, :members, :trackers + fixtures :projects, :repositories, :issues, :issue_statuses, :changesets, :changes, :issue_categories, :enumerations, :custom_fields, :custom_values, :users, :members, :member_roles, :trackers def setup end diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb index 5052f7a0c..d836f2bb5 100644 --- a/test/unit/issue_test.rb +++ b/test/unit/issue_test.rb @@ -18,7 +18,7 @@ require File.dirname(__FILE__) + '/../test_helper' class IssueTest < Test::Unit::TestCase - fixtures :projects, :users, :members, + fixtures :projects, :users, :members, :member_roles, :trackers, :projects_trackers, :issue_statuses, :issue_categories, :enumerations, @@ -242,6 +242,10 @@ class IssueTest < Test::Unit::TestCase assert !Issue.new(:due_date => 1.day.ago.to_date, :status => IssueStatus.find(:first, :conditions => {:is_closed => true})).overdue? end + def test_assignable_users + assert_kind_of User, Issue.find(1).assignable_users.first + end + def test_create_should_send_email_notification ActionMailer::Base.deliveries.clear issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => Enumeration.priorities.first, :subject => 'test_create', :estimated_hours => '1:30') diff --git a/test/unit/mail_handler_test.rb b/test/unit/mail_handler_test.rb index 704e12c7f..295b8ae68 100644 --- a/test/unit/mail_handler_test.rb +++ b/test/unit/mail_handler_test.rb @@ -22,6 +22,7 @@ class MailHandlerTest < Test::Unit::TestCase :enabled_modules, :roles, :members, + :member_roles, :issues, :issue_statuses, :workflows, diff --git a/test/unit/mailer_test.rb b/test/unit/mailer_test.rb index d15af6e03..b1c39bb5e 100644 --- a/test/unit/mailer_test.rb +++ b/test/unit/mailer_test.rb @@ -19,7 +19,7 @@ require File.dirname(__FILE__) + '/../test_helper' class MailerTest < Test::Unit::TestCase include Redmine::I18n - fixtures :projects, :issues, :users, :members, :documents, :attachments, :news, :tokens, :journals, :journal_details, :changesets, :trackers, :issue_statuses, :enumerations, :messages, :boards, :repositories + fixtures :projects, :issues, :users, :members, :member_roles, :documents, :attachments, :news, :tokens, :journals, :journal_details, :changesets, :trackers, :issue_statuses, :enumerations, :messages, :boards, :repositories def test_generated_links_in_emails ActionMailer::Base.deliveries.clear diff --git a/test/unit/member_test.rb b/test/unit/member_test.rb index 079782306..66f1283ca 100644 --- a/test/unit/member_test.rb +++ b/test/unit/member_test.rb @@ -18,34 +18,54 @@ require File.dirname(__FILE__) + '/../test_helper' class MemberTest < Test::Unit::TestCase - fixtures :users, :projects, :roles, :members + fixtures :users, :projects, :roles, :members, :member_roles def setup @jsmith = Member.find(1) end def test_create - member = Member.new(:project_id => 1, :user_id => 4, :role_id => 1) - assert member.save + member = Member.new(:project_id => 1, :user_id => 4, :role_ids => [1, 2]) + assert member.save + member.reload + + assert_equal 2, member.roles.size + assert_equal Role.find(1), member.roles.sort.first end def test_update assert_equal "eCookbook", @jsmith.project.name - assert_equal "Manager", @jsmith.role.name + assert_equal "Manager", @jsmith.roles.first.name assert_equal "jsmith", @jsmith.user.login - @jsmith.role = Role.find(2) + @jsmith.mail_notification = !@jsmith.mail_notification assert @jsmith.save end + + def test_update_roles + assert_equal 1, @jsmith.roles.size + @jsmith.role_ids = [1, 2] + assert @jsmith.save + assert_equal 2, @jsmith.reload.roles.size + end def test_validate - member = Member.new(:project_id => 1, :user_id => 2, :role_id =>2) - # same use can't have more than one role for a project + member = Member.new(:project_id => 1, :user_id => 2, :role_ids => [2]) + # same use can't have more than one membership for a project + assert !member.save + + member = Member.new(:project_id => 1, :user_id => 2, :role_ids => []) + # must have one role at least assert !member.save end - def test_destroy - @jsmith.destroy + def test_destroy + assert_difference 'Member.count', -1 do + assert_difference 'MemberRole.count', -1 do + @jsmith.destroy + end + end + assert_raise(ActiveRecord::RecordNotFound) { Member.find(@jsmith.id) } end end diff --git a/test/unit/message_test.rb b/test/unit/message_test.rb index bc9bd5fd3..d88e98bcf 100644 --- a/test/unit/message_test.rb +++ b/test/unit/message_test.rb @@ -18,7 +18,7 @@ require File.dirname(__FILE__) + '/../test_helper' class MessageTest < Test::Unit::TestCase - fixtures :projects, :roles, :members, :boards, :messages, :users, :watchers + fixtures :projects, :roles, :members, :member_roles, :boards, :messages, :users, :watchers def setup @board = Board.find(1) @@ -116,7 +116,7 @@ class MessageTest < Test::Unit::TestCase author = message.author assert message.editable_by?(author) - author.role_for_project(message.project).remove_permission!(:edit_own_messages) + author.roles_for_project(message.project).first.remove_permission!(:edit_own_messages) assert !message.reload.editable_by?(author.reload) end @@ -125,7 +125,7 @@ class MessageTest < Test::Unit::TestCase author = message.author assert message.destroyable_by?(author) - author.role_for_project(message.project).remove_permission!(:delete_own_messages) + author.roles_for_project(message.project).first.remove_permission!(:delete_own_messages) assert !message.reload.destroyable_by?(author.reload) end end diff --git a/test/unit/news_test.rb b/test/unit/news_test.rb index 3a908dcc8..a4fc89e90 100644 --- a/test/unit/news_test.rb +++ b/test/unit/news_test.rb @@ -18,7 +18,7 @@ require File.dirname(__FILE__) + '/../test_helper' class NewsTest < Test::Unit::TestCase - fixtures :projects, :users, :roles, :members, :enabled_modules, :news + fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules, :news def valid_news { :title => 'Test news', :description => 'Lorem ipsum etc', :author => User.find(:first) } diff --git a/test/unit/project_test.rb b/test/unit/project_test.rb index f9a17e2ec..4ecd9d4a8 100644 --- a/test/unit/project_test.rb +++ b/test/unit/project_test.rb @@ -20,8 +20,8 @@ require File.dirname(__FILE__) + '/../test_helper' class ProjectTest < Test::Unit::TestCase fixtures :projects, :enabled_modules, :issues, :issue_statuses, :journals, :journal_details, - :users, :members, :roles, :projects_trackers, :trackers, :boards, - :queries + :users, :members, :member_roles, :roles, :projects_trackers, :trackers, :boards, + :queries def setup @ecookbook = Project.find(1) diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb index 80112b779..e12bac8d7 100644 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -18,7 +18,7 @@ require File.dirname(__FILE__) + '/../test_helper' class QueryTest < Test::Unit::TestCase - fixtures :projects, :enabled_modules, :users, :members, :roles, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :watchers, :custom_fields, :custom_values, :versions, :queries + fixtures :projects, :enabled_modules, :users, :members, :member_roles, :roles, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :watchers, :custom_fields, :custom_values, :versions, :queries def test_custom_fields_for_all_projects_should_be_available_in_global_queries query = Query.new(:project => nil, :name => '_') diff --git a/test/unit/search_test.rb b/test/unit/search_test.rb index 1b32df733..46d769451 100644 --- a/test/unit/search_test.rb +++ b/test/unit/search_test.rb @@ -19,7 +19,8 @@ require File.dirname(__FILE__) + '/../test_helper' class SearchTest < Test::Unit::TestCase fixtures :users, - :members, + :members, + :member_roles, :projects, :roles, :enabled_modules, diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb index f6d270410..7080c0b5b 100644 --- a/test/unit/user_test.rb +++ b/test/unit/user_test.rb @@ -18,7 +18,7 @@ require File.dirname(__FILE__) + '/../test_helper' class UserTest < Test::Unit::TestCase - fixtures :users, :members, :projects + fixtures :users, :members, :projects, :roles, :member_roles def setup @admin = User.find(1) @@ -130,14 +130,14 @@ class UserTest < Test::Unit::TestCase assert_equal key, @jsmith.rss_key end - def test_role_for_project + def test_roles_for_project # user with a role - role = @jsmith.role_for_project(Project.find(1)) - assert_kind_of Role, role - assert_equal "Manager", role.name + roles = @jsmith.roles_for_project(Project.find(1)) + assert_kind_of Role, roles.first + assert_equal "Manager", roles.first.name # user with no role - assert !@dlopper.role_for_project(Project.find(2)).member? + assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?} end def test_mail_notification_all