##// END OF EJS Templates
Merged r13334 (#17486)....
Jean-Philippe Lang -
r13026:5f184f9fd1da
parent child
Show More
@@ -1,126 +1,126
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Member < ActiveRecord::Base
18 class Member < ActiveRecord::Base
19 belongs_to :user
19 belongs_to :user
20 belongs_to :principal, :foreign_key => 'user_id'
20 belongs_to :principal, :foreign_key => 'user_id'
21 has_many :member_roles, :dependent => :destroy
21 has_many :member_roles, :dependent => :destroy
22 has_many :roles, :through => :member_roles
22 has_many :roles, :through => :member_roles
23 belongs_to :project
23 belongs_to :project
24
24
25 validates_presence_of :principal, :project
25 validates_presence_of :principal, :project
26 validates_uniqueness_of :user_id, :scope => :project_id
26 validates_uniqueness_of :user_id, :scope => :project_id
27 validate :validate_role
27 validate :validate_role
28
28
29 before_destroy :set_issue_category_nil
29 before_destroy :set_issue_category_nil
30
30
31 def role
31 def role
32 end
32 end
33
33
34 def role=
34 def role=
35 end
35 end
36
36
37 def name
37 def name
38 self.user.name
38 self.user.name
39 end
39 end
40
40
41 alias :base_role_ids= :role_ids=
41 alias :base_role_ids= :role_ids=
42 def role_ids=(arg)
42 def role_ids=(arg)
43 ids = (arg || []).collect(&:to_i) - [0]
43 ids = (arg || []).collect(&:to_i) - [0]
44 # Keep inherited roles
44 # Keep inherited roles
45 ids += member_roles.select {|mr| !mr.inherited_from.nil?}.collect(&:role_id)
45 ids += member_roles.select {|mr| !mr.inherited_from.nil?}.collect(&:role_id)
46
46
47 new_role_ids = ids - role_ids
47 new_role_ids = ids - role_ids
48 # Add new roles
48 # Add new roles
49 new_role_ids.each {|id| member_roles << MemberRole.new(:role_id => id) }
49 new_role_ids.each {|id| member_roles << MemberRole.new(:role_id => id) }
50 # Remove roles (Rails' #role_ids= will not trigger MemberRole#on_destroy)
50 # Remove roles (Rails' #role_ids= will not trigger MemberRole#on_destroy)
51 member_roles_to_destroy = member_roles.select {|mr| !ids.include?(mr.role_id)}
51 member_roles_to_destroy = member_roles.select {|mr| !ids.include?(mr.role_id)}
52 if member_roles_to_destroy.any?
52 if member_roles_to_destroy.any?
53 member_roles_to_destroy.each(&:destroy)
53 member_roles_to_destroy.each(&:destroy)
54 end
54 end
55 end
55 end
56
56
57 def <=>(member)
57 def <=>(member)
58 a, b = roles.sort.first, member.roles.sort.first
58 a, b = roles.sort.first, member.roles.sort.first
59 if a == b
59 if a == b
60 if principal
60 if principal
61 principal <=> member.principal
61 principal <=> member.principal
62 else
62 else
63 1
63 1
64 end
64 end
65 elsif a
65 elsif a
66 a <=> b
66 a <=> b
67 else
67 else
68 1
68 1
69 end
69 end
70 end
70 end
71
71
72 def deletable?
72 def deletable?
73 member_roles.detect {|mr| mr.inherited_from}.nil?
73 member_roles.detect {|mr| mr.inherited_from}.nil?
74 end
74 end
75
75
76 def destroy
76 def destroy
77 if member_roles.reload.present?
77 if member_roles.reload.present?
78 # destroying the last role will destroy another instance
78 # destroying the last role will destroy another instance
79 # of the same Member record, #super would then trigger callbacks twice
79 # of the same Member record, #super would then trigger callbacks twice
80 member_roles.destroy_all
80 member_roles.destroy_all
81 @destroyed = true
81 @destroyed = true
82 freeze
82 freeze
83 else
83 else
84 super
84 super
85 end
85 end
86 end
86 end
87
87
88 def include?(user)
88 def include?(user)
89 if principal.is_a?(Group)
89 if principal.is_a?(Group)
90 !user.nil? && user.groups.include?(principal)
90 !user.nil? && user.groups.include?(principal)
91 else
91 else
92 self.user == user
92 self.user == user
93 end
93 end
94 end
94 end
95
95
96 def set_issue_category_nil
96 def set_issue_category_nil
97 if user
97 if user_id && project_id
98 # remove category based auto assignments for this member
98 # remove category based auto assignments for this member
99 IssueCategory.where(["project_id = ? AND assigned_to_id = ?", project.id, user.id]).
99 IssueCategory.where(["project_id = ? AND assigned_to_id = ?", project_id, user_id]).
100 update_all("assigned_to_id = NULL")
100 update_all("assigned_to_id = NULL")
101 end
101 end
102 end
102 end
103
103
104 # Find or initilize a Member with an id, attributes, and for a Principal
104 # Find or initilize a Member with an id, attributes, and for a Principal
105 def self.edit_membership(id, new_attributes, principal=nil)
105 def self.edit_membership(id, new_attributes, principal=nil)
106 @membership = id.present? ? Member.find(id) : Member.new(:principal => principal)
106 @membership = id.present? ? Member.find(id) : Member.new(:principal => principal)
107 @membership.attributes = new_attributes
107 @membership.attributes = new_attributes
108 @membership
108 @membership
109 end
109 end
110
110
111 # Finds or initilizes a Member for the given project and principal
111 # Finds or initilizes a Member for the given project and principal
112 def self.find_or_new(project, principal)
112 def self.find_or_new(project, principal)
113 project_id = project.is_a?(Project) ? project.id : project
113 project_id = project.is_a?(Project) ? project.id : project
114 principal_id = principal.is_a?(Principal) ? principal.id : principal
114 principal_id = principal.is_a?(Principal) ? principal.id : principal
115
115
116 member = Member.find_by_project_id_and_user_id(project_id, principal_id)
116 member = Member.find_by_project_id_and_user_id(project_id, principal_id)
117 member ||= Member.new(:project_id => project_id, :user_id => principal_id)
117 member ||= Member.new(:project_id => project_id, :user_id => principal_id)
118 member
118 member
119 end
119 end
120
120
121 protected
121 protected
122
122
123 def validate_role
123 def validate_role
124 errors.add_on_empty :role if member_roles.empty? && roles.empty?
124 errors.add_on_empty :role if member_roles.empty? && roles.empty?
125 end
125 end
126 end
126 end
@@ -1,143 +1,153
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class MemberTest < ActiveSupport::TestCase
20 class MemberTest < ActiveSupport::TestCase
21 fixtures :projects, :trackers, :issue_statuses, :issues,
21 fixtures :projects, :trackers, :issue_statuses, :issues,
22 :enumerations, :users, :issue_categories,
22 :enumerations, :users, :issue_categories,
23 :projects_trackers,
23 :projects_trackers,
24 :roles,
24 :roles,
25 :member_roles,
25 :member_roles,
26 :members,
26 :members,
27 :enabled_modules,
27 :enabled_modules,
28 :groups_users,
28 :groups_users,
29 :watchers,
29 :watchers,
30 :journals, :journal_details,
30 :journals, :journal_details,
31 :messages,
31 :messages,
32 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
32 :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
33 :boards
33 :boards
34
34
35 include Redmine::I18n
35 include Redmine::I18n
36
36
37 def setup
37 def setup
38 @jsmith = Member.find(1)
38 @jsmith = Member.find(1)
39 end
39 end
40
40
41 def test_create
41 def test_create
42 member = Member.new(:project_id => 1, :user_id => 4, :role_ids => [1, 2])
42 member = Member.new(:project_id => 1, :user_id => 4, :role_ids => [1, 2])
43 assert member.save
43 assert member.save
44 member.reload
44 member.reload
45
45
46 assert_equal 2, member.roles.size
46 assert_equal 2, member.roles.size
47 assert_equal Role.find(1), member.roles.sort.first
47 assert_equal Role.find(1), member.roles.sort.first
48 end
48 end
49
49
50 def test_update
50 def test_update
51 assert_equal "eCookbook", @jsmith.project.name
51 assert_equal "eCookbook", @jsmith.project.name
52 assert_equal "Manager", @jsmith.roles.first.name
52 assert_equal "Manager", @jsmith.roles.first.name
53 assert_equal "jsmith", @jsmith.user.login
53 assert_equal "jsmith", @jsmith.user.login
54
54
55 @jsmith.mail_notification = !@jsmith.mail_notification
55 @jsmith.mail_notification = !@jsmith.mail_notification
56 assert @jsmith.save
56 assert @jsmith.save
57 end
57 end
58
58
59 def test_update_roles
59 def test_update_roles
60 assert_equal 1, @jsmith.roles.size
60 assert_equal 1, @jsmith.roles.size
61 @jsmith.role_ids = [1, 2]
61 @jsmith.role_ids = [1, 2]
62 assert @jsmith.save
62 assert @jsmith.save
63 assert_equal 2, @jsmith.reload.roles.size
63 assert_equal 2, @jsmith.reload.roles.size
64 end
64 end
65
65
66 def test_validate
66 def test_validate
67 member = Member.new(:project_id => 1, :user_id => 2, :role_ids => [2])
67 member = Member.new(:project_id => 1, :user_id => 2, :role_ids => [2])
68 # same use can't have more than one membership for a project
68 # same use can't have more than one membership for a project
69 assert !member.save
69 assert !member.save
70
70
71 # must have one role at least
71 # must have one role at least
72 user = User.new(:firstname => "new1", :lastname => "user1",
72 user = User.new(:firstname => "new1", :lastname => "user1",
73 :mail => "test_validate@somenet.foo")
73 :mail => "test_validate@somenet.foo")
74 user.login = "test_validate"
74 user.login = "test_validate"
75 user.password, user.password_confirmation = "password", "password"
75 user.password, user.password_confirmation = "password", "password"
76 assert user.save
76 assert user.save
77
77
78 set_language_if_valid 'fr'
78 set_language_if_valid 'fr'
79 member = Member.new(:project_id => 1, :user_id => user.id, :role_ids => [])
79 member = Member.new(:project_id => 1, :user_id => user.id, :role_ids => [])
80 assert !member.save
80 assert !member.save
81 assert_include I18n.translate('activerecord.errors.messages.empty'), member.errors[:role]
81 assert_include I18n.translate('activerecord.errors.messages.empty'), member.errors[:role]
82 str = "R\xc3\xb4le doit \xc3\xaatre renseign\xc3\xa9(e)"
82 str = "R\xc3\xb4le doit \xc3\xaatre renseign\xc3\xa9(e)"
83 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
83 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
84 assert_equal str, [member.errors.full_messages].flatten.join
84 assert_equal str, [member.errors.full_messages].flatten.join
85 end
85 end
86
86
87 def test_validate_member_role
87 def test_validate_member_role
88 user = User.new(:firstname => "new1", :lastname => "user1",
88 user = User.new(:firstname => "new1", :lastname => "user1",
89 :mail => "test_validate@somenet.foo")
89 :mail => "test_validate@somenet.foo")
90 user.login = "test_validate_member_role"
90 user.login = "test_validate_member_role"
91 user.password, user.password_confirmation = "password", "password"
91 user.password, user.password_confirmation = "password", "password"
92 assert user.save
92 assert user.save
93 member = Member.new(:project_id => 1, :user_id => user.id, :role_ids => [5])
93 member = Member.new(:project_id => 1, :user_id => user.id, :role_ids => [5])
94 assert !member.save
94 assert !member.save
95 end
95 end
96
96
97 def test_set_issue_category_nil_should_handle_nil_values
98 m = Member.new
99 assert_nil m.user
100 assert_nil m.project
101
102 assert_nothing_raised do
103 m.set_issue_category_nil
104 end
105 end
106
97 def test_destroy
107 def test_destroy
98 category1 = IssueCategory.find(1)
108 category1 = IssueCategory.find(1)
99 assert_equal @jsmith.user.id, category1.assigned_to_id
109 assert_equal @jsmith.user.id, category1.assigned_to_id
100 assert_difference 'Member.count', -1 do
110 assert_difference 'Member.count', -1 do
101 assert_difference 'MemberRole.count', -1 do
111 assert_difference 'MemberRole.count', -1 do
102 @jsmith.destroy
112 @jsmith.destroy
103 end
113 end
104 end
114 end
105 assert_raise(ActiveRecord::RecordNotFound) { Member.find(@jsmith.id) }
115 assert_raise(ActiveRecord::RecordNotFound) { Member.find(@jsmith.id) }
106 category1.reload
116 category1.reload
107 assert_nil category1.assigned_to_id
117 assert_nil category1.assigned_to_id
108 end
118 end
109
119
110 def test_destroy_should_trigger_callbacks_only_once
120 def test_destroy_should_trigger_callbacks_only_once
111 Member.class_eval { def destroy_test_callback; end}
121 Member.class_eval { def destroy_test_callback; end}
112 Member.after_destroy :destroy_test_callback
122 Member.after_destroy :destroy_test_callback
113
123
114 m = Member.create!(:user_id => 1, :project_id => 1, :role_ids => [1,3])
124 m = Member.create!(:user_id => 1, :project_id => 1, :role_ids => [1,3])
115
125
116 Member.any_instance.expects(:destroy_test_callback).once
126 Member.any_instance.expects(:destroy_test_callback).once
117 assert_difference 'Member.count', -1 do
127 assert_difference 'Member.count', -1 do
118 assert_difference 'MemberRole.count', -2 do
128 assert_difference 'MemberRole.count', -2 do
119 m.destroy
129 m.destroy
120 end
130 end
121 end
131 end
122 assert m.destroyed?
132 assert m.destroyed?
123 ensure
133 ensure
124 Member._destroy_callbacks.reject! {|c| c.filter==:destroy_test_callback}
134 Member._destroy_callbacks.reject! {|c| c.filter==:destroy_test_callback}
125 end
135 end
126
136
127 def test_sort_without_roles
137 def test_sort_without_roles
128 a = Member.new(:roles => [Role.first])
138 a = Member.new(:roles => [Role.first])
129 b = Member.new
139 b = Member.new
130
140
131 assert_equal -1, a <=> b
141 assert_equal -1, a <=> b
132 assert_equal 1, b <=> a
142 assert_equal 1, b <=> a
133 end
143 end
134
144
135 def test_sort_without_principal
145 def test_sort_without_principal
136 role = Role.first
146 role = Role.first
137 a = Member.new(:roles => [role], :principal => User.first)
147 a = Member.new(:roles => [role], :principal => User.first)
138 b = Member.new(:roles => [role])
148 b = Member.new(:roles => [role])
139
149
140 assert_equal -1, a <=> b
150 assert_equal -1, a <=> b
141 assert_equal 1, b <=> a
151 assert_equal 1, b <=> a
142 end
152 end
143 end
153 end
General Comments 0
You need to be logged in to leave comments. Login now