diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2009-09-12 08:36:46 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2009-09-12 08:36:46 +0000 |
commit | 7707457145442d6177ce57c956dbe09af65df1b4 (patch) | |
tree | 2278f27bf20e08bb2f92e42ab728b4427cce6f07 /app/models | |
parent | 847c7367b429e8df0e0fa1dbf3e415e37dd82bf1 (diff) | |
download | redmine-7707457145442d6177ce57c956dbe09af65df1b4.tar.gz redmine-7707457145442d6177ce57c956dbe09af65df1b4.zip |
User groups branch merged.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2869 e93f8b46-1217-0410-a6f0-8f06a7374b81
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/group.rb | 48 | ||||
-rw-r--r-- | app/models/group_custom_field.rb | 22 | ||||
-rw-r--r-- | app/models/member.rb | 38 | ||||
-rw-r--r-- | app/models/member_role.rb | 27 | ||||
-rw-r--r-- | app/models/principal.rb | 38 | ||||
-rw-r--r-- | app/models/project.rb | 7 | ||||
-rw-r--r-- | app/models/user.rb | 15 |
7 files changed, 172 insertions, 23 deletions
diff --git a/app/models/group.rb b/app/models/group.rb new file mode 100644 index 000000000..80e096be8 --- /dev/null +++ b/app/models/group.rb @@ -0,0 +1,48 @@ +# 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 Group < Principal + has_and_belongs_to_many :users, :after_add => :user_added, + :after_remove => :user_removed + + acts_as_customizable + + validates_presence_of :lastname + validates_uniqueness_of :lastname, :case_sensitive => false + validates_length_of :lastname, :maximum => 30 + + def to_s + lastname.to_s + end + + def user_added(user) + members.each do |member| + user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id) || Member.new(:project_id => member.project_id, :user_id => user.id) + member.member_roles.each do |member_role| + user_member.member_roles << MemberRole.new(:role => member_role.role, :inherited_from => member_role.id) + end + user_member.save! + end + end + + def user_removed(user) + members.each do |member| + MemberRole.find(:all, :include => :member, + :conditions => ["#{Member.table_name}.user_id = ? AND #{MemberRole.table_name}.inherited_from IN (?)", user.id, member.member_role_ids]).each(&:destroy) + end + end +end diff --git a/app/models/group_custom_field.rb b/app/models/group_custom_field.rb new file mode 100644 index 000000000..b7c199cbf --- /dev/null +++ b/app/models/group_custom_field.rb @@ -0,0 +1,22 @@ +# 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 GroupCustomField < CustomField + def type_name + :label_group_plural + end +end diff --git a/app/models/member.rb b/app/models/member.rb index 2dc91cba7..6fffb2161 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -17,40 +17,50 @@ class Member < ActiveRecord::Base belongs_to :user - has_many :member_roles, :dependent => :delete_all + belongs_to :principal, :foreign_key => 'user_id' + has_many :member_roles, :dependent => :destroy has_many :roles, :through => :member_roles belongs_to :project - validates_presence_of :user, :project + validates_presence_of :principal, :project validates_uniqueness_of :user_id, :scope => :project_id def name self.user.name end - # Sets user by login - def user_login=(login) - login = login.to_s - unless login.blank? - if (u = User.find_by_login(login)) - self.user = u - end - end + alias :base_role_ids= :role_ids= + def role_ids=(arg) + ids = (arg || []).collect(&:to_i) - [0] + # Keep inherited roles + ids += member_roles.select {|mr| !mr.inherited_from.nil?}.collect(&:role_id) + + new_role_ids = ids - role_ids + # Add new roles + new_role_ids.each {|id| member_roles << MemberRole.new(:role_id => id) } + # Remove roles (Rails' #role_ids= will not trigger MemberRole#on_destroy) + member_roles.select {|mr| !ids.include?(mr.role_id)}.each(&:destroy) end def <=>(member) a, b = roles.sort.first, member.roles.sort.first - a == b ? (user <=> member.user) : (a <=> b) + a == b ? (principal <=> member.principal) : (a <=> b) + end + + def deletable? + member_roles.detect {|mr| mr.inherited_from}.nil? 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] + if user + # 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 end protected def validate - errors.add_to_base "Role can't be blank" if roles.empty? + errors.add_to_base "Role can't be blank" if member_roles.empty? && roles.empty? end end diff --git a/app/models/member_role.rb b/app/models/member_role.rb index 46777cd1e..5a31c17c5 100644 --- a/app/models/member_role.rb +++ b/app/models/member_role.rb @@ -19,9 +19,36 @@ class MemberRole < ActiveRecord::Base belongs_to :member belongs_to :role + after_destroy :remove_member_if_empty + + after_create :add_role_to_group_users + after_destroy :remove_role_from_group_users + validates_presence_of :role def validate errors.add :role_id, :invalid if role && !role.member? end + + private + + def remove_member_if_empty + if member.roles.empty? + member.destroy + end + end + + def add_role_to_group_users + if member.principal.is_a?(Group) + member.principal.users.each do |user| + user_member = Member.find_by_project_id_and_user_id(member.project_id, user.id) || Member.new(:project_id => member.project_id, :user_id => user.id) + user_member.member_roles << MemberRole.new(:role => role, :inherited_from => id) + user_member.save! + end + end + end + + def remove_role_from_group_users + MemberRole.find(:all, :conditions => { :inherited_from => id }).each(&:destroy) + end end diff --git a/app/models/principal.rb b/app/models/principal.rb new file mode 100644 index 000000000..a4a946da5 --- /dev/null +++ b/app/models/principal.rb @@ -0,0 +1,38 @@ +# 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 Principal < ActiveRecord::Base
+ set_table_name 'users'
+
+ has_many :members, :foreign_key => 'user_id', :dependent => :destroy
+ has_many :memberships, :class_name => 'Member', :foreign_key => 'user_id', :include => [ :project, :roles ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name"
+ has_many :projects, :through => :memberships
+
+ # Groups and active users
+ named_scope :active, :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status = 1)"
+
+ named_scope :like, lambda {|q|
+ s = "%#{q.to_s.strip.downcase}%"
+ {:conditions => ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ?", s, s, s],
+ :order => 'type, login, lastname, firstname'
+ }
+ }
+
+ def <=>(principal)
+ self.to_s.downcase <=> principal.to_s.downcase
+ end
+end
diff --git a/app/models/project.rb b/app/models/project.rb index 0b0c47a24..a6b1ee482 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -20,8 +20,13 @@ class Project < ActiveRecord::Base STATUS_ACTIVE = 1 STATUS_ARCHIVED = 9 - has_many :members, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}" + has_many :members, :include => :user, :conditions => "#{User.table_name}.type='User' AND #{User.table_name}.status=#{User::STATUS_ACTIVE}" + has_many :member_principals, :class_name => 'Member', + :include => :principal, + :conditions => "#{Principal.table_name}.type='Group' OR (#{Principal.table_name}.type='User' AND #{Principal.table_name}.status=#{User::STATUS_ACTIVE})" has_many :users, :through => :members + has_many :principals, :through => :member_principals, :source => :principal + has_many :enabled_modules, :dependent => :delete_all has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position" has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker] diff --git a/app/models/user.rb b/app/models/user.rb index 0caaf34f6..6922cb51a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,5 @@ -# redMine - project management software -# Copyright (C) 2006-2007 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,7 +17,7 @@ require "digest/sha1" -class User < ActiveRecord::Base +class User < Principal # Account statuses STATUS_ANONYMOUS = 0 @@ -33,9 +33,8 @@ class User < ActiveRecord::Base :username => '#{login}' } - 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_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)}, + :after_remove => Proc.new {|user, group| group.user_removed(user)} has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify has_many :changesets, :dependent => :nullify has_one :preference, :dependent => :destroy, :class_name => 'UserPreference' @@ -50,7 +49,7 @@ class User < ActiveRecord::Base attr_accessor :password, :password_confirmation attr_accessor :last_before_login_on # Prevents unauthorized assignments - attr_protected :login, :admin, :password, :password_confirmation, :hashed_password + attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) } validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? } @@ -317,7 +316,7 @@ class User < ActiveRecord::Base end private - + # Return password digest def self.hash_password(clear_password) Digest::SHA1.hexdigest(clear_password || "") |