diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2009-05-10 10:54:31 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2009-05-10 10:54:31 +0000 |
commit | 7dccf9fda6f30d8f4b0c5eaad9f6e2a1e67cd643 (patch) | |
tree | ba6a18abff6ca69af528b3d295263d049a22266f /app/models | |
parent | 814e138c2a1105f8d9d10c4362a889dd71aff32d (diff) | |
download | redmine-7dccf9fda6f30d8f4b0c5eaad9f6e2a1e67cd643.tar.gz redmine-7dccf9fda6f30d8f4b0c5eaad9f6e2a1e67cd643.zip |
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
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/issue.rb | 2 | ||||
-rw-r--r-- | app/models/issue_status.rb | 32 | ||||
-rw-r--r-- | app/models/member.rb | 22 | ||||
-rw-r--r-- | app/models/member_role.rb | 27 | ||||
-rw-r--r-- | app/models/project.rb | 6 | ||||
-rw-r--r-- | app/models/role.rb | 9 | ||||
-rw-r--r-- | app/models/user.rb | 24 |
7 files changed, 87 insertions, 35 deletions
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 |