##// END OF EJS Templates
Fixed: Calendar and Gantt show private subprojects even if current user is not a member of them (#1217)....
Jean-Philippe Lang -
r1416:7ee38a95a005
parent child
Show More
@@ -1,256 +1,257
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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 Project < ActiveRecord::Base
18 class Project < ActiveRecord::Base
19 # Project statuses
19 # Project statuses
20 STATUS_ACTIVE = 1
20 STATUS_ACTIVE = 1
21 STATUS_ARCHIVED = 9
21 STATUS_ARCHIVED = 9
22
22
23 has_many :members, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}"
23 has_many :members, :include => :user, :conditions => "#{User.table_name}.status=#{User::STATUS_ACTIVE}"
24 has_many :users, :through => :members
24 has_many :users, :through => :members
25 has_many :custom_values, :dependent => :delete_all, :as => :customized
25 has_many :custom_values, :dependent => :delete_all, :as => :customized
26 has_many :enabled_modules, :dependent => :delete_all
26 has_many :enabled_modules, :dependent => :delete_all
27 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
27 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
28 has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker]
28 has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker]
29 has_many :issue_changes, :through => :issues, :source => :journals
29 has_many :issue_changes, :through => :issues, :source => :journals
30 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
30 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
31 has_many :time_entries, :dependent => :delete_all
31 has_many :time_entries, :dependent => :delete_all
32 has_many :queries, :dependent => :delete_all
32 has_many :queries, :dependent => :delete_all
33 has_many :documents, :dependent => :destroy
33 has_many :documents, :dependent => :destroy
34 has_many :news, :dependent => :delete_all, :include => :author
34 has_many :news, :dependent => :delete_all, :include => :author
35 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
35 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
36 has_many :boards, :dependent => :destroy, :order => "position ASC"
36 has_many :boards, :dependent => :destroy, :order => "position ASC"
37 has_one :repository, :dependent => :destroy
37 has_one :repository, :dependent => :destroy
38 has_many :changesets, :through => :repository
38 has_many :changesets, :through => :repository
39 has_one :wiki, :dependent => :destroy
39 has_one :wiki, :dependent => :destroy
40 # Custom field for the project issues
40 # Custom field for the project issues
41 has_and_belongs_to_many :custom_fields,
41 has_and_belongs_to_many :custom_fields,
42 :class_name => 'IssueCustomField',
42 :class_name => 'IssueCustomField',
43 :order => "#{CustomField.table_name}.position",
43 :order => "#{CustomField.table_name}.position",
44 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
44 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
45 :association_foreign_key => 'custom_field_id'
45 :association_foreign_key => 'custom_field_id'
46
46
47 acts_as_tree :order => "name", :counter_cache => true
47 acts_as_tree :order => "name", :counter_cache => true
48
48
49 acts_as_searchable :columns => ['name', 'description'], :project_key => 'id'
49 acts_as_searchable :columns => ['name', 'description'], :project_key => 'id'
50 acts_as_event :title => Proc.new {|o| "#{l(:label_project)}: #{o.name}"},
50 acts_as_event :title => Proc.new {|o| "#{l(:label_project)}: #{o.name}"},
51 :url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o.id}}
51 :url => Proc.new {|o| {:controller => 'projects', :action => 'show', :id => o.id}}
52
52
53 attr_protected :status, :enabled_module_names
53 attr_protected :status, :enabled_module_names
54
54
55 validates_presence_of :name, :identifier
55 validates_presence_of :name, :identifier
56 validates_uniqueness_of :name, :identifier
56 validates_uniqueness_of :name, :identifier
57 validates_associated :custom_values, :on => :update
57 validates_associated :custom_values, :on => :update
58 validates_associated :repository, :wiki
58 validates_associated :repository, :wiki
59 validates_length_of :name, :maximum => 30
59 validates_length_of :name, :maximum => 30
60 validates_length_of :homepage, :maximum => 60
60 validates_length_of :homepage, :maximum => 60
61 validates_length_of :identifier, :in => 3..20
61 validates_length_of :identifier, :in => 3..20
62 validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
62 validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
63
63
64 before_destroy :delete_all_members
64 before_destroy :delete_all_members
65
65
66 def identifier=(identifier)
66 def identifier=(identifier)
67 super unless identifier_frozen?
67 super unless identifier_frozen?
68 end
68 end
69
69
70 def identifier_frozen?
70 def identifier_frozen?
71 errors[:identifier].nil? && !(new_record? || identifier.blank?)
71 errors[:identifier].nil? && !(new_record? || identifier.blank?)
72 end
72 end
73
73
74 def issues_with_subprojects(include_subprojects=false)
74 def issues_with_subprojects(include_subprojects=false)
75 conditions = nil
75 conditions = nil
76 if include_subprojects && !active_children.empty?
76 if include_subprojects
77 ids = [id] + active_children.collect {|c| c.id}
77 ids = [id] + child_ids
78 conditions = ["#{Project.table_name}.id IN (#{ids.join(',')})"]
78 conditions = ["#{Project.table_name}.id IN (#{ids.join(',')}) AND #{Project.visible_by}"]
79 end
79 end
80 conditions ||= ["#{Project.table_name}.id = ?", id]
80 conditions ||= ["#{Project.table_name}.id = ?", id]
81 # Quick and dirty fix for Rails 2 compatibility
81 # Quick and dirty fix for Rails 2 compatibility
82 Issue.send(:with_scope, :find => { :conditions => conditions }) do
82 Issue.send(:with_scope, :find => { :conditions => conditions }) do
83 Version.send(:with_scope, :find => { :conditions => conditions }) do
83 Version.send(:with_scope, :find => { :conditions => conditions }) do
84 yield
84 yield
85 end
85 end
86 end
86 end
87 end
87 end
88
88
89 # returns latest created projects
89 # returns latest created projects
90 # non public projects will be returned only if user is a member of those
90 # non public projects will be returned only if user is a member of those
91 def self.latest(user=nil, count=5)
91 def self.latest(user=nil, count=5)
92 find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC")
92 find(:all, :limit => count, :conditions => visible_by(user), :order => "created_on DESC")
93 end
93 end
94
94
95 def self.visible_by(user=nil)
95 def self.visible_by(user=nil)
96 user ||= User.current
96 if user && user.admin?
97 if user && user.admin?
97 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
98 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
98 elsif user && user.memberships.any?
99 elsif user && user.memberships.any?
99 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND (#{Project.table_name}.is_public = #{connection.quoted_true} or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')}))"
100 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND (#{Project.table_name}.is_public = #{connection.quoted_true} or #{Project.table_name}.id IN (#{user.memberships.collect{|m| m.project_id}.join(',')}))"
100 else
101 else
101 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = #{connection.quoted_true}"
102 return "#{Project.table_name}.status=#{Project::STATUS_ACTIVE} AND #{Project.table_name}.is_public = #{connection.quoted_true}"
102 end
103 end
103 end
104 end
104
105
105 def self.allowed_to_condition(user, permission, options={})
106 def self.allowed_to_condition(user, permission, options={})
106 statements = []
107 statements = []
107 base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
108 base_statement = "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"
108 if options[:project]
109 if options[:project]
109 project_statement = "#{Project.table_name}.id = #{options[:project].id}"
110 project_statement = "#{Project.table_name}.id = #{options[:project].id}"
110 project_statement << " OR #{Project.table_name}.parent_id = #{options[:project].id}" if options[:with_subprojects]
111 project_statement << " OR #{Project.table_name}.parent_id = #{options[:project].id}" if options[:with_subprojects]
111 base_statement = "(#{project_statement}) AND (#{base_statement})"
112 base_statement = "(#{project_statement}) AND (#{base_statement})"
112 end
113 end
113 if user.admin?
114 if user.admin?
114 # no restriction
115 # no restriction
115 elsif user.logged?
116 elsif user.logged?
116 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" if Role.non_member.allowed_to?(permission)
117 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" if Role.non_member.allowed_to?(permission)
117 allowed_project_ids = user.memberships.select {|m| m.role.allowed_to?(permission)}.collect {|m| m.project_id}
118 allowed_project_ids = user.memberships.select {|m| m.role.allowed_to?(permission)}.collect {|m| m.project_id}
118 statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any?
119 statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any?
119 elsif Role.anonymous.allowed_to?(permission)
120 elsif Role.anonymous.allowed_to?(permission)
120 # anonymous user allowed on public project
121 # anonymous user allowed on public project
121 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}"
122 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}"
122 else
123 else
123 # anonymous user is not authorized
124 # anonymous user is not authorized
124 statements << "1=0"
125 statements << "1=0"
125 end
126 end
126 statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))"
127 statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))"
127 end
128 end
128
129
129 def project_condition(with_subprojects)
130 def project_condition(with_subprojects)
130 cond = "#{Project.table_name}.id = #{id}"
131 cond = "#{Project.table_name}.id = #{id}"
131 cond = "(#{cond} OR #{Project.table_name}.parent_id = #{id})" if with_subprojects
132 cond = "(#{cond} OR #{Project.table_name}.parent_id = #{id})" if with_subprojects
132 cond
133 cond
133 end
134 end
134
135
135 def self.find(*args)
136 def self.find(*args)
136 if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
137 if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
137 project = find_by_identifier(*args)
138 project = find_by_identifier(*args)
138 raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil?
139 raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil?
139 project
140 project
140 else
141 else
141 super
142 super
142 end
143 end
143 end
144 end
144
145
145 def to_param
146 def to_param
146 identifier
147 identifier
147 end
148 end
148
149
149 def active?
150 def active?
150 self.status == STATUS_ACTIVE
151 self.status == STATUS_ACTIVE
151 end
152 end
152
153
153 def archive
154 def archive
154 # Archive subprojects if any
155 # Archive subprojects if any
155 children.each do |subproject|
156 children.each do |subproject|
156 subproject.archive
157 subproject.archive
157 end
158 end
158 update_attribute :status, STATUS_ARCHIVED
159 update_attribute :status, STATUS_ARCHIVED
159 end
160 end
160
161
161 def unarchive
162 def unarchive
162 return false if parent && !parent.active?
163 return false if parent && !parent.active?
163 update_attribute :status, STATUS_ACTIVE
164 update_attribute :status, STATUS_ACTIVE
164 end
165 end
165
166
166 def active_children
167 def active_children
167 children.select {|child| child.active?}
168 children.select {|child| child.active?}
168 end
169 end
169
170
170 # Returns an array of the trackers used by the project and its sub projects
171 # Returns an array of the trackers used by the project and its sub projects
171 def rolled_up_trackers
172 def rolled_up_trackers
172 @rolled_up_trackers ||=
173 @rolled_up_trackers ||=
173 Tracker.find(:all, :include => :projects,
174 Tracker.find(:all, :include => :projects,
174 :select => "DISTINCT #{Tracker.table_name}.*",
175 :select => "DISTINCT #{Tracker.table_name}.*",
175 :conditions => ["#{Project.table_name}.id = ? OR #{Project.table_name}.parent_id = ?", id, id],
176 :conditions => ["#{Project.table_name}.id = ? OR #{Project.table_name}.parent_id = ?", id, id],
176 :order => "#{Tracker.table_name}.position")
177 :order => "#{Tracker.table_name}.position")
177 end
178 end
178
179
179 # Deletes all project's members
180 # Deletes all project's members
180 def delete_all_members
181 def delete_all_members
181 Member.delete_all(['project_id = ?', id])
182 Member.delete_all(['project_id = ?', id])
182 end
183 end
183
184
184 # Users issues can be assigned to
185 # Users issues can be assigned to
185 def assignable_users
186 def assignable_users
186 members.select {|m| m.role.assignable?}.collect {|m| m.user}.sort
187 members.select {|m| m.role.assignable?}.collect {|m| m.user}.sort
187 end
188 end
188
189
189 # Returns the mail adresses of users that should be always notified on project events
190 # Returns the mail adresses of users that should be always notified on project events
190 def recipients
191 def recipients
191 members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail}
192 members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail}
192 end
193 end
193
194
194 # Returns an array of all custom fields enabled for project issues
195 # Returns an array of all custom fields enabled for project issues
195 # (explictly associated custom fields and custom fields enabled for all projects)
196 # (explictly associated custom fields and custom fields enabled for all projects)
196 def custom_fields_for_issues(tracker)
197 def custom_fields_for_issues(tracker)
197 all_custom_fields.select {|c| tracker.custom_fields.include? c }
198 all_custom_fields.select {|c| tracker.custom_fields.include? c }
198 end
199 end
199
200
200 def all_custom_fields
201 def all_custom_fields
201 @all_custom_fields ||= (IssueCustomField.for_all + custom_fields).uniq
202 @all_custom_fields ||= (IssueCustomField.for_all + custom_fields).uniq
202 end
203 end
203
204
204 def <=>(project)
205 def <=>(project)
205 name.downcase <=> project.name.downcase
206 name.downcase <=> project.name.downcase
206 end
207 end
207
208
208 def to_s
209 def to_s
209 name
210 name
210 end
211 end
211
212
212 # Returns a short description of the projects (first lines)
213 # Returns a short description of the projects (first lines)
213 def short_description(length = 255)
214 def short_description(length = 255)
214 description.gsub(/^(.{#{length}}[^\n]*).*$/m, '\1').strip if description
215 description.gsub(/^(.{#{length}}[^\n]*).*$/m, '\1').strip if description
215 end
216 end
216
217
217 def allows_to?(action)
218 def allows_to?(action)
218 if action.is_a? Hash
219 if action.is_a? Hash
219 allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
220 allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
220 else
221 else
221 allowed_permissions.include? action
222 allowed_permissions.include? action
222 end
223 end
223 end
224 end
224
225
225 def module_enabled?(module_name)
226 def module_enabled?(module_name)
226 module_name = module_name.to_s
227 module_name = module_name.to_s
227 enabled_modules.detect {|m| m.name == module_name}
228 enabled_modules.detect {|m| m.name == module_name}
228 end
229 end
229
230
230 def enabled_module_names=(module_names)
231 def enabled_module_names=(module_names)
231 enabled_modules.clear
232 enabled_modules.clear
232 module_names = [] unless module_names && module_names.is_a?(Array)
233 module_names = [] unless module_names && module_names.is_a?(Array)
233 module_names.each do |name|
234 module_names.each do |name|
234 enabled_modules << EnabledModule.new(:name => name.to_s)
235 enabled_modules << EnabledModule.new(:name => name.to_s)
235 end
236 end
236 end
237 end
237
238
238 protected
239 protected
239 def validate
240 def validate
240 errors.add(parent_id, " must be a root project") if parent and parent.parent
241 errors.add(parent_id, " must be a root project") if parent and parent.parent
241 errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
242 errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
242 errors.add(:identifier, :activerecord_error_invalid) if !identifier.blank? && identifier.match(/^\d*$/)
243 errors.add(:identifier, :activerecord_error_invalid) if !identifier.blank? && identifier.match(/^\d*$/)
243 end
244 end
244
245
245 private
246 private
246 def allowed_permissions
247 def allowed_permissions
247 @allowed_permissions ||= begin
248 @allowed_permissions ||= begin
248 module_names = enabled_modules.collect {|m| m.name}
249 module_names = enabled_modules.collect {|m| m.name}
249 Redmine::AccessControl.modules_permissions(module_names).collect {|p| p.name}
250 Redmine::AccessControl.modules_permissions(module_names).collect {|p| p.name}
250 end
251 end
251 end
252 end
252
253
253 def allowed_actions
254 def allowed_actions
254 @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
255 @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
255 end
256 end
256 end
257 end
@@ -1,74 +1,90
1 ---
1 ---
2 issues_001:
2 issues_001:
3 created_on: <%= 3.days.ago.to_date.to_s(:db) %>
3 created_on: <%= 3.days.ago.to_date.to_s(:db) %>
4 project_id: 1
4 project_id: 1
5 updated_on: <%= 1.day.ago.to_date.to_s(:db) %>
5 updated_on: <%= 1.day.ago.to_date.to_s(:db) %>
6 priority_id: 4
6 priority_id: 4
7 subject: Can't print recipes
7 subject: Can't print recipes
8 id: 1
8 id: 1
9 fixed_version_id:
9 fixed_version_id:
10 category_id: 1
10 category_id: 1
11 description: Unable to print recipes
11 description: Unable to print recipes
12 tracker_id: 1
12 tracker_id: 1
13 assigned_to_id:
13 assigned_to_id:
14 author_id: 2
14 author_id: 2
15 status_id: 1
15 status_id: 1
16 issues_002:
16 issues_002:
17 created_on: 2006-07-19 21:04:21 +02:00
17 created_on: 2006-07-19 21:04:21 +02:00
18 project_id: 1
18 project_id: 1
19 updated_on: 2006-07-19 21:09:50 +02:00
19 updated_on: 2006-07-19 21:09:50 +02:00
20 priority_id: 5
20 priority_id: 5
21 subject: Add ingredients categories
21 subject: Add ingredients categories
22 id: 2
22 id: 2
23 fixed_version_id:
23 fixed_version_id:
24 category_id:
24 category_id:
25 description: Ingredients of the recipe should be classified by categories
25 description: Ingredients of the recipe should be classified by categories
26 tracker_id: 2
26 tracker_id: 2
27 assigned_to_id: 3
27 assigned_to_id: 3
28 author_id: 2
28 author_id: 2
29 status_id: 2
29 status_id: 2
30 issues_003:
30 issues_003:
31 created_on: 2006-07-19 21:07:27 +02:00
31 created_on: 2006-07-19 21:07:27 +02:00
32 project_id: 1
32 project_id: 1
33 updated_on: 2006-07-19 21:07:27 +02:00
33 updated_on: 2006-07-19 21:07:27 +02:00
34 priority_id: 4
34 priority_id: 4
35 subject: Error 281 when updating a recipe
35 subject: Error 281 when updating a recipe
36 id: 3
36 id: 3
37 fixed_version_id:
37 fixed_version_id:
38 category_id:
38 category_id:
39 description: Error 281 is encountered when saving a recipe
39 description: Error 281 is encountered when saving a recipe
40 tracker_id: 1
40 tracker_id: 1
41 assigned_to_id:
41 assigned_to_id:
42 author_id: 2
42 author_id: 2
43 status_id: 1
43 status_id: 1
44 start_date: <%= 1.day.from_now.to_date.to_s(:db) %>
44 start_date: <%= 1.day.from_now.to_date.to_s(:db) %>
45 due_date: <%= 40.day.ago.to_date.to_s(:db) %>
45 due_date: <%= 40.day.ago.to_date.to_s(:db) %>
46 issues_004:
46 issues_004:
47 created_on: <%= 5.days.ago.to_date.to_s(:db) %>
47 created_on: <%= 5.days.ago.to_date.to_s(:db) %>
48 project_id: 2
48 project_id: 2
49 updated_on: <%= 2.days.ago.to_date.to_s(:db) %>
49 updated_on: <%= 2.days.ago.to_date.to_s(:db) %>
50 priority_id: 4
50 priority_id: 4
51 subject: Issue on project 2
51 subject: Issue on project 2
52 id: 4
52 id: 4
53 fixed_version_id:
53 fixed_version_id:
54 category_id:
54 category_id:
55 description: Issue on project 2
55 description: Issue on project 2
56 tracker_id: 1
56 tracker_id: 1
57 assigned_to_id:
57 assigned_to_id:
58 author_id: 2
58 author_id: 2
59 status_id: 1
59 status_id: 1
60 issues_005:
60 issues_005:
61 created_on: <%= 5.days.ago.to_date.to_s(:db) %>
61 created_on: <%= 5.days.ago.to_date.to_s(:db) %>
62 project_id: 3
62 project_id: 3
63 updated_on: <%= 2.days.ago.to_date.to_s(:db) %>
63 updated_on: <%= 2.days.ago.to_date.to_s(:db) %>
64 priority_id: 4
64 priority_id: 4
65 subject: Subproject issue
65 subject: Subproject issue
66 id: 5
66 id: 5
67 fixed_version_id:
67 fixed_version_id:
68 category_id:
68 category_id:
69 description: This is an issue on a cookbook subproject
69 description: This is an issue on a cookbook subproject
70 tracker_id: 1
70 tracker_id: 1
71 assigned_to_id:
71 assigned_to_id:
72 author_id: 2
72 author_id: 2
73 status_id: 1
73 status_id: 1
74
74 issues_006:
75 created_on: <%= 1.minute.ago.to_date.to_s(:db) %>
76 project_id: 5
77 updated_on: <%= 1.minute.ago.to_date.to_s(:db) %>
78 priority_id: 4
79 subject: Issue of a private subproject
80 id: 6
81 fixed_version_id:
82 category_id:
83 description: This is an issue of a private subproject of cookbook
84 tracker_id: 1
85 assigned_to_id:
86 author_id: 2
87 status_id: 1
88 start_date: <%= Date.today.to_s(:db) %>
89 due_date: <%= 1.days.from_now.to_date.to_s(:db) %>
90 No newline at end of file
@@ -1,283 +1,304
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'projects_controller'
19 require 'projects_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class ProjectsController; def rescue_action(e) raise e end; end
22 class ProjectsController; def rescue_action(e) raise e end; end
23
23
24 class ProjectsControllerTest < Test::Unit::TestCase
24 class ProjectsControllerTest < Test::Unit::TestCase
25 fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details,
25 fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details,
26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages
26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages
27
27
28 def setup
28 def setup
29 @controller = ProjectsController.new
29 @controller = ProjectsController.new
30 @request = ActionController::TestRequest.new
30 @request = ActionController::TestRequest.new
31 @response = ActionController::TestResponse.new
31 @response = ActionController::TestResponse.new
32 @request.session[:user_id] = nil
32 end
33 end
33
34
34 def test_index
35 def test_index
35 get :index
36 get :index
36 assert_response :success
37 assert_response :success
37 assert_template 'list'
38 assert_template 'list'
38 end
39 end
39
40
40 def test_list
41 def test_list
41 get :list
42 get :list
42 assert_response :success
43 assert_response :success
43 assert_template 'list'
44 assert_template 'list'
44 assert_not_nil assigns(:project_tree)
45 assert_not_nil assigns(:project_tree)
45 # Root project as hash key
46 # Root project as hash key
46 assert assigns(:project_tree).has_key?(Project.find(1))
47 assert assigns(:project_tree).has_key?(Project.find(1))
47 # Subproject in corresponding value
48 # Subproject in corresponding value
48 assert assigns(:project_tree)[Project.find(1)].include?(Project.find(3))
49 assert assigns(:project_tree)[Project.find(1)].include?(Project.find(3))
49 end
50 end
50
51
51 def test_show_by_id
52 def test_show_by_id
52 get :show, :id => 1
53 get :show, :id => 1
53 assert_response :success
54 assert_response :success
54 assert_template 'show'
55 assert_template 'show'
55 assert_not_nil assigns(:project)
56 assert_not_nil assigns(:project)
56 end
57 end
57
58
58 def test_show_by_identifier
59 def test_show_by_identifier
59 get :show, :id => 'ecookbook'
60 get :show, :id => 'ecookbook'
60 assert_response :success
61 assert_response :success
61 assert_template 'show'
62 assert_template 'show'
62 assert_not_nil assigns(:project)
63 assert_not_nil assigns(:project)
63 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
64 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
64 end
65 end
65
66
66 def test_private_subprojects_hidden
67 def test_private_subprojects_hidden
67 get :show, :id => 'ecookbook'
68 get :show, :id => 'ecookbook'
68 assert_response :success
69 assert_response :success
69 assert_template 'show'
70 assert_template 'show'
70 assert_no_tag :tag => 'a', :content => /Private child/
71 assert_no_tag :tag => 'a', :content => /Private child/
71 end
72 end
72
73
73 def test_private_subprojects_visible
74 def test_private_subprojects_visible
74 @request.session[:user_id] = 2 # manager who is a member of the private subproject
75 @request.session[:user_id] = 2 # manager who is a member of the private subproject
75 get :show, :id => 'ecookbook'
76 get :show, :id => 'ecookbook'
76 assert_response :success
77 assert_response :success
77 assert_template 'show'
78 assert_template 'show'
78 assert_tag :tag => 'a', :content => /Private child/
79 assert_tag :tag => 'a', :content => /Private child/
79 end
80 end
80
81
81 def test_settings
82 def test_settings
82 @request.session[:user_id] = 2 # manager
83 @request.session[:user_id] = 2 # manager
83 get :settings, :id => 1
84 get :settings, :id => 1
84 assert_response :success
85 assert_response :success
85 assert_template 'settings'
86 assert_template 'settings'
86 end
87 end
87
88
88 def test_edit
89 def test_edit
89 @request.session[:user_id] = 2 # manager
90 @request.session[:user_id] = 2 # manager
90 post :edit, :id => 1, :project => {:name => 'Test changed name',
91 post :edit, :id => 1, :project => {:name => 'Test changed name',
91 :custom_field_ids => ['']}
92 :custom_field_ids => ['']}
92 assert_redirected_to 'projects/settings/ecookbook'
93 assert_redirected_to 'projects/settings/ecookbook'
93 project = Project.find(1)
94 project = Project.find(1)
94 assert_equal 'Test changed name', project.name
95 assert_equal 'Test changed name', project.name
95 end
96 end
96
97
97 def test_get_destroy
98 def test_get_destroy
98 @request.session[:user_id] = 1 # admin
99 @request.session[:user_id] = 1 # admin
99 get :destroy, :id => 1
100 get :destroy, :id => 1
100 assert_response :success
101 assert_response :success
101 assert_template 'destroy'
102 assert_template 'destroy'
102 assert_not_nil Project.find_by_id(1)
103 assert_not_nil Project.find_by_id(1)
103 end
104 end
104
105
105 def test_post_destroy
106 def test_post_destroy
106 @request.session[:user_id] = 1 # admin
107 @request.session[:user_id] = 1 # admin
107 post :destroy, :id => 1, :confirm => 1
108 post :destroy, :id => 1, :confirm => 1
108 assert_redirected_to 'admin/projects'
109 assert_redirected_to 'admin/projects'
109 assert_nil Project.find_by_id(1)
110 assert_nil Project.find_by_id(1)
110 end
111 end
111
112
112 def test_list_files
113 def test_list_files
113 get :list_files, :id => 1
114 get :list_files, :id => 1
114 assert_response :success
115 assert_response :success
115 assert_template 'list_files'
116 assert_template 'list_files'
116 assert_not_nil assigns(:versions)
117 assert_not_nil assigns(:versions)
117 end
118 end
118
119
119 def test_changelog
120 def test_changelog
120 get :changelog, :id => 1
121 get :changelog, :id => 1
121 assert_response :success
122 assert_response :success
122 assert_template 'changelog'
123 assert_template 'changelog'
123 assert_not_nil assigns(:versions)
124 assert_not_nil assigns(:versions)
124 end
125 end
125
126
126 def test_roadmap
127 def test_roadmap
127 get :roadmap, :id => 1
128 get :roadmap, :id => 1
128 assert_response :success
129 assert_response :success
129 assert_template 'roadmap'
130 assert_template 'roadmap'
130 assert_not_nil assigns(:versions)
131 assert_not_nil assigns(:versions)
131 # Version with no date set appears
132 # Version with no date set appears
132 assert assigns(:versions).include?(Version.find(3))
133 assert assigns(:versions).include?(Version.find(3))
133 # Completed version doesn't appear
134 # Completed version doesn't appear
134 assert !assigns(:versions).include?(Version.find(1))
135 assert !assigns(:versions).include?(Version.find(1))
135 end
136 end
136
137
137 def test_roadmap_with_completed_versions
138 def test_roadmap_with_completed_versions
138 get :roadmap, :id => 1, :completed => 1
139 get :roadmap, :id => 1, :completed => 1
139 assert_response :success
140 assert_response :success
140 assert_template 'roadmap'
141 assert_template 'roadmap'
141 assert_not_nil assigns(:versions)
142 assert_not_nil assigns(:versions)
142 # Version with no date set appears
143 # Version with no date set appears
143 assert assigns(:versions).include?(Version.find(3))
144 assert assigns(:versions).include?(Version.find(3))
144 # Completed version appears
145 # Completed version appears
145 assert assigns(:versions).include?(Version.find(1))
146 assert assigns(:versions).include?(Version.find(1))
146 end
147 end
147
148
148 def test_project_activity
149 def test_project_activity
149 get :activity, :id => 1, :with_subprojects => 0
150 get :activity, :id => 1, :with_subprojects => 0
150 assert_response :success
151 assert_response :success
151 assert_template 'activity'
152 assert_template 'activity'
152 assert_not_nil assigns(:events_by_day)
153 assert_not_nil assigns(:events_by_day)
153 assert_not_nil assigns(:events)
154 assert_not_nil assigns(:events)
154
155
155 # subproject issue not included by default
156 # subproject issue not included by default
156 assert !assigns(:events).include?(Issue.find(5))
157 assert !assigns(:events).include?(Issue.find(5))
157
158
158 assert_tag :tag => "h3",
159 assert_tag :tag => "h3",
159 :content => /#{2.days.ago.to_date.day}/,
160 :content => /#{2.days.ago.to_date.day}/,
160 :sibling => { :tag => "dl",
161 :sibling => { :tag => "dl",
161 :child => { :tag => "dt",
162 :child => { :tag => "dt",
162 :attributes => { :class => 'issue-edit' },
163 :attributes => { :class => 'issue-edit' },
163 :child => { :tag => "a",
164 :child => { :tag => "a",
164 :content => /(#{IssueStatus.find(2).name})/,
165 :content => /(#{IssueStatus.find(2).name})/,
165 }
166 }
166 }
167 }
167 }
168 }
168
169
169 get :activity, :id => 1, :from => 3.days.ago.to_date
170 get :activity, :id => 1, :from => 3.days.ago.to_date
170 assert_response :success
171 assert_response :success
171 assert_template 'activity'
172 assert_template 'activity'
172 assert_not_nil assigns(:events_by_day)
173 assert_not_nil assigns(:events_by_day)
173
174
174 assert_tag :tag => "h3",
175 assert_tag :tag => "h3",
175 :content => /#{3.day.ago.to_date.day}/,
176 :content => /#{3.day.ago.to_date.day}/,
176 :sibling => { :tag => "dl",
177 :sibling => { :tag => "dl",
177 :child => { :tag => "dt",
178 :child => { :tag => "dt",
178 :attributes => { :class => 'issue' },
179 :attributes => { :class => 'issue' },
179 :child => { :tag => "a",
180 :child => { :tag => "a",
180 :content => /#{Issue.find(1).subject}/,
181 :content => /#{Issue.find(1).subject}/,
181 }
182 }
182 }
183 }
183 }
184 }
184 end
185 end
185
186
186 def test_activity_with_subprojects
187 def test_activity_with_subprojects
187 get :activity, :id => 1, :with_subprojects => 1
188 get :activity, :id => 1, :with_subprojects => 1
188 assert_response :success
189 assert_response :success
189 assert_template 'activity'
190 assert_template 'activity'
190 assert_not_nil assigns(:events)
191 assert_not_nil assigns(:events)
191
192
192 assert assigns(:events).include?(Issue.find(1))
193 assert assigns(:events).include?(Issue.find(1))
193 assert !assigns(:events).include?(Issue.find(4))
194 assert !assigns(:events).include?(Issue.find(4))
194 # subproject issue
195 # subproject issue
195 assert assigns(:events).include?(Issue.find(5))
196 assert assigns(:events).include?(Issue.find(5))
196 end
197 end
197
198
198 def test_global_activity_anonymous
199 def test_global_activity_anonymous
199 get :activity
200 get :activity
200 assert_response :success
201 assert_response :success
201 assert_template 'activity'
202 assert_template 'activity'
202 assert_not_nil assigns(:events)
203 assert_not_nil assigns(:events)
203
204
204 assert assigns(:events).include?(Issue.find(1))
205 assert assigns(:events).include?(Issue.find(1))
205 # Issue of a private project
206 # Issue of a private project
206 assert !assigns(:events).include?(Issue.find(4))
207 assert !assigns(:events).include?(Issue.find(4))
207 end
208 end
208
209
209 def test_global_activity_logged_user
210 def test_global_activity_logged_user
210 @request.session[:user_id] = 2 # manager
211 @request.session[:user_id] = 2 # manager
211 get :activity
212 get :activity
212 assert_response :success
213 assert_response :success
213 assert_template 'activity'
214 assert_template 'activity'
214 assert_not_nil assigns(:events)
215 assert_not_nil assigns(:events)
215
216
216 assert assigns(:events).include?(Issue.find(1))
217 assert assigns(:events).include?(Issue.find(1))
217 # Issue of a private project the user belongs to
218 # Issue of a private project the user belongs to
218 assert assigns(:events).include?(Issue.find(4))
219 assert assigns(:events).include?(Issue.find(4))
219 end
220 end
220
221
221
222
222 def test_global_activity_with_all_types
223 def test_global_activity_with_all_types
223 get :activity, :show_issues => 1, :show_news => 1, :show_files => 1, :show_documents => 1, :show_changesets => 1, :show_wiki_pages => 1, :show_messages => 1
224 get :activity, :show_issues => 1, :show_news => 1, :show_files => 1, :show_documents => 1, :show_changesets => 1, :show_wiki_pages => 1, :show_messages => 1
224 assert_response :success
225 assert_response :success
225 assert_template 'activity'
226 assert_template 'activity'
226 assert_not_nil assigns(:events)
227 assert_not_nil assigns(:events)
227
228
228 assert assigns(:events).include?(Issue.find(1))
229 assert assigns(:events).include?(Issue.find(1))
229 assert !assigns(:events).include?(Issue.find(4))
230 assert !assigns(:events).include?(Issue.find(4))
230 assert assigns(:events).include?(Message.find(5))
231 assert assigns(:events).include?(Message.find(5))
231 end
232 end
232
233
233 def test_calendar
234 def test_calendar
234 get :calendar, :id => 1
235 get :calendar, :id => 1
235 assert_response :success
236 assert_response :success
236 assert_template 'calendar'
237 assert_template 'calendar'
237 assert_not_nil assigns(:calendar)
238 assert_not_nil assigns(:calendar)
238 end
239 end
239
240
240 def test_calendar_with_subprojects
241 def test_calendar_with_subprojects_should_not_show_private_subprojects
241 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
242 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
242 assert_response :success
243 assert_response :success
243 assert_template 'calendar'
244 assert_template 'calendar'
244 assert_not_nil assigns(:calendar)
245 assert_not_nil assigns(:calendar)
246 assert_no_tag :tag => 'a', :content => /#6/
247 end
248
249 def test_calendar_with_subprojects_should_show_private_subprojects
250 @request.session[:user_id] = 2
251 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
252 assert_response :success
253 assert_template 'calendar'
254 assert_not_nil assigns(:calendar)
255 assert_tag :tag => 'a', :content => /#6/
245 end
256 end
246
257
247 def test_gantt
258 def test_gantt
248 get :gantt, :id => 1
259 get :gantt, :id => 1
249 assert_response :success
260 assert_response :success
250 assert_template 'gantt.rhtml'
261 assert_template 'gantt.rhtml'
251 assert_not_nil assigns(:events)
262 assert_not_nil assigns(:events)
252 end
263 end
253
264
254 def test_gantt_with_subprojects
265 def test_gantt_with_subprojects_should_not_show_private_subprojects
255 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
266 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
256 assert_response :success
267 assert_response :success
257 assert_template 'gantt.rhtml'
268 assert_template 'gantt.rhtml'
258 assert_not_nil assigns(:events)
269 assert_not_nil assigns(:events)
270 assert_no_tag :tag => 'a', :content => /#6/
259 end
271 end
260
272
273 def test_gantt_with_subprojects_should_show_private_subprojects
274 @request.session[:user_id] = 2
275 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
276 assert_response :success
277 assert_template 'gantt.rhtml'
278 assert_not_nil assigns(:events)
279 assert_tag :tag => 'a', :content => /#6/
280 end
281
261 def test_gantt_export_to_pdf
282 def test_gantt_export_to_pdf
262 get :gantt, :id => 1, :format => 'pdf'
283 get :gantt, :id => 1, :format => 'pdf'
263 assert_response :success
284 assert_response :success
264 assert_template 'gantt.rfpdf'
285 assert_template 'gantt.rfpdf'
265 assert_equal 'application/pdf', @response.content_type
286 assert_equal 'application/pdf', @response.content_type
266 assert_not_nil assigns(:events)
287 assert_not_nil assigns(:events)
267 end
288 end
268
289
269 def test_archive
290 def test_archive
270 @request.session[:user_id] = 1 # admin
291 @request.session[:user_id] = 1 # admin
271 post :archive, :id => 1
292 post :archive, :id => 1
272 assert_redirected_to 'admin/projects'
293 assert_redirected_to 'admin/projects'
273 assert !Project.find(1).active?
294 assert !Project.find(1).active?
274 end
295 end
275
296
276 def test_unarchive
297 def test_unarchive
277 @request.session[:user_id] = 1 # admin
298 @request.session[:user_id] = 1 # admin
278 Project.find(1).archive
299 Project.find(1).archive
279 post :unarchive, :id => 1
300 post :unarchive, :id => 1
280 assert_redirected_to 'admin/projects'
301 assert_redirected_to 'admin/projects'
281 assert Project.find(1).active?
302 assert Project.find(1).active?
282 end
303 end
283 end
304 end
General Comments 0
You need to be logged in to leave comments. Login now