##// END OF EJS Templates
Fixed: no :author method error on projects atom feed (#1623)....
Jean-Philippe Lang -
r1639:b5444b5fcd84
parent child
Show More
@@ -1,259 +1,260
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 :enabled_modules, :dependent => :delete_all
25 has_many :enabled_modules, :dependent => :delete_all
26 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
26 has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
27 has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker]
27 has_many :issues, :dependent => :destroy, :order => "#{Issue.table_name}.created_on DESC", :include => [:status, :tracker]
28 has_many :issue_changes, :through => :issues, :source => :journals
28 has_many :issue_changes, :through => :issues, :source => :journals
29 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
29 has_many :versions, :dependent => :destroy, :order => "#{Version.table_name}.effective_date DESC, #{Version.table_name}.name DESC"
30 has_many :time_entries, :dependent => :delete_all
30 has_many :time_entries, :dependent => :delete_all
31 has_many :queries, :dependent => :delete_all
31 has_many :queries, :dependent => :delete_all
32 has_many :documents, :dependent => :destroy
32 has_many :documents, :dependent => :destroy
33 has_many :news, :dependent => :delete_all, :include => :author
33 has_many :news, :dependent => :delete_all, :include => :author
34 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
34 has_many :issue_categories, :dependent => :delete_all, :order => "#{IssueCategory.table_name}.name"
35 has_many :boards, :dependent => :destroy, :order => "position ASC"
35 has_many :boards, :dependent => :destroy, :order => "position ASC"
36 has_one :repository, :dependent => :destroy
36 has_one :repository, :dependent => :destroy
37 has_many :changesets, :through => :repository
37 has_many :changesets, :through => :repository
38 has_one :wiki, :dependent => :destroy
38 has_one :wiki, :dependent => :destroy
39 # Custom field for the project issues
39 # Custom field for the project issues
40 has_and_belongs_to_many :issue_custom_fields,
40 has_and_belongs_to_many :issue_custom_fields,
41 :class_name => 'IssueCustomField',
41 :class_name => 'IssueCustomField',
42 :order => "#{CustomField.table_name}.position",
42 :order => "#{CustomField.table_name}.position",
43 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
43 :join_table => "#{table_name_prefix}custom_fields_projects#{table_name_suffix}",
44 :association_foreign_key => 'custom_field_id'
44 :association_foreign_key => 'custom_field_id'
45
45
46 acts_as_tree :order => "name", :counter_cache => true
46 acts_as_tree :order => "name", :counter_cache => true
47
47
48 acts_as_customizable
48 acts_as_customizable
49 acts_as_searchable :columns => ['name', 'description'], :project_key => 'id', :permission => nil
49 acts_as_searchable :columns => ['name', 'description'], :project_key => 'id', :permission => nil
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 :author => nil
52
53
53 attr_protected :status, :enabled_module_names
54 attr_protected :status, :enabled_module_names
54
55
55 validates_presence_of :name, :identifier
56 validates_presence_of :name, :identifier
56 validates_uniqueness_of :name, :identifier
57 validates_uniqueness_of :name, :identifier
57 validates_associated :repository, :wiki
58 validates_associated :repository, :wiki
58 validates_length_of :name, :maximum => 30
59 validates_length_of :name, :maximum => 30
59 validates_length_of :homepage, :maximum => 255
60 validates_length_of :homepage, :maximum => 255
60 validates_length_of :identifier, :in => 3..20
61 validates_length_of :identifier, :in => 3..20
61 validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
62 validates_format_of :identifier, :with => /^[a-z0-9\-]*$/
62
63
63 before_destroy :delete_all_members
64 before_destroy :delete_all_members
64
65
65 def identifier=(identifier)
66 def identifier=(identifier)
66 super unless identifier_frozen?
67 super unless identifier_frozen?
67 end
68 end
68
69
69 def identifier_frozen?
70 def identifier_frozen?
70 errors[:identifier].nil? && !(new_record? || identifier.blank?)
71 errors[:identifier].nil? && !(new_record? || identifier.blank?)
71 end
72 end
72
73
73 def issues_with_subprojects(include_subprojects=false)
74 def issues_with_subprojects(include_subprojects=false)
74 conditions = nil
75 conditions = nil
75 if include_subprojects
76 if include_subprojects
76 ids = [id] + child_ids
77 ids = [id] + child_ids
77 conditions = ["#{Project.table_name}.id IN (#{ids.join(',')}) AND #{Project.visible_by}"]
78 conditions = ["#{Project.table_name}.id IN (#{ids.join(',')}) AND #{Project.visible_by}"]
78 end
79 end
79 conditions ||= ["#{Project.table_name}.id = ?", id]
80 conditions ||= ["#{Project.table_name}.id = ?", id]
80 # Quick and dirty fix for Rails 2 compatibility
81 # Quick and dirty fix for Rails 2 compatibility
81 Issue.send(:with_scope, :find => { :conditions => conditions }) do
82 Issue.send(:with_scope, :find => { :conditions => conditions }) do
82 Version.send(:with_scope, :find => { :conditions => conditions }) do
83 Version.send(:with_scope, :find => { :conditions => conditions }) do
83 yield
84 yield
84 end
85 end
85 end
86 end
86 end
87 end
87
88
88 # returns latest created projects
89 # returns latest created projects
89 # 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
90 def self.latest(user=nil, count=5)
91 def self.latest(user=nil, count=5)
91 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")
92 end
93 end
93
94
94 def self.visible_by(user=nil)
95 def self.visible_by(user=nil)
95 user ||= User.current
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 else
116 else
116 statements << "1=0"
117 statements << "1=0"
117 if user.logged?
118 if user.logged?
118 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" if Role.non_member.allowed_to?(permission)
119 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}" if Role.non_member.allowed_to?(permission)
119 allowed_project_ids = user.memberships.select {|m| m.role.allowed_to?(permission)}.collect {|m| m.project_id}
120 allowed_project_ids = user.memberships.select {|m| m.role.allowed_to?(permission)}.collect {|m| m.project_id}
120 statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any?
121 statements << "#{Project.table_name}.id IN (#{allowed_project_ids.join(',')})" if allowed_project_ids.any?
121 elsif Role.anonymous.allowed_to?(permission)
122 elsif Role.anonymous.allowed_to?(permission)
122 # anonymous user allowed on public project
123 # anonymous user allowed on public project
123 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}"
124 statements << "#{Project.table_name}.is_public = #{connection.quoted_true}"
124 else
125 else
125 # anonymous user is not authorized
126 # anonymous user is not authorized
126 end
127 end
127 end
128 end
128 statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))"
129 statements.empty? ? base_statement : "((#{base_statement}) AND (#{statements.join(' OR ')}))"
129 end
130 end
130
131
131 def project_condition(with_subprojects)
132 def project_condition(with_subprojects)
132 cond = "#{Project.table_name}.id = #{id}"
133 cond = "#{Project.table_name}.id = #{id}"
133 cond = "(#{cond} OR #{Project.table_name}.parent_id = #{id})" if with_subprojects
134 cond = "(#{cond} OR #{Project.table_name}.parent_id = #{id})" if with_subprojects
134 cond
135 cond
135 end
136 end
136
137
137 def self.find(*args)
138 def self.find(*args)
138 if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
139 if args.first && args.first.is_a?(String) && !args.first.match(/^\d*$/)
139 project = find_by_identifier(*args)
140 project = find_by_identifier(*args)
140 raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil?
141 raise ActiveRecord::RecordNotFound, "Couldn't find Project with identifier=#{args.first}" if project.nil?
141 project
142 project
142 else
143 else
143 super
144 super
144 end
145 end
145 end
146 end
146
147
147 def to_param
148 def to_param
148 # id is used for projects with a numeric identifier (compatibility)
149 # id is used for projects with a numeric identifier (compatibility)
149 @to_param ||= (identifier.to_s =~ %r{^\d*$} ? id : identifier)
150 @to_param ||= (identifier.to_s =~ %r{^\d*$} ? id : identifier)
150 end
151 end
151
152
152 def active?
153 def active?
153 self.status == STATUS_ACTIVE
154 self.status == STATUS_ACTIVE
154 end
155 end
155
156
156 def archive
157 def archive
157 # Archive subprojects if any
158 # Archive subprojects if any
158 children.each do |subproject|
159 children.each do |subproject|
159 subproject.archive
160 subproject.archive
160 end
161 end
161 update_attribute :status, STATUS_ARCHIVED
162 update_attribute :status, STATUS_ARCHIVED
162 end
163 end
163
164
164 def unarchive
165 def unarchive
165 return false if parent && !parent.active?
166 return false if parent && !parent.active?
166 update_attribute :status, STATUS_ACTIVE
167 update_attribute :status, STATUS_ACTIVE
167 end
168 end
168
169
169 def active_children
170 def active_children
170 children.select {|child| child.active?}
171 children.select {|child| child.active?}
171 end
172 end
172
173
173 # Returns an array of the trackers used by the project and its sub projects
174 # Returns an array of the trackers used by the project and its sub projects
174 def rolled_up_trackers
175 def rolled_up_trackers
175 @rolled_up_trackers ||=
176 @rolled_up_trackers ||=
176 Tracker.find(:all, :include => :projects,
177 Tracker.find(:all, :include => :projects,
177 :select => "DISTINCT #{Tracker.table_name}.*",
178 :select => "DISTINCT #{Tracker.table_name}.*",
178 :conditions => ["#{Project.table_name}.id = ? OR #{Project.table_name}.parent_id = ?", id, id],
179 :conditions => ["#{Project.table_name}.id = ? OR #{Project.table_name}.parent_id = ?", id, id],
179 :order => "#{Tracker.table_name}.position")
180 :order => "#{Tracker.table_name}.position")
180 end
181 end
181
182
182 # Deletes all project's members
183 # Deletes all project's members
183 def delete_all_members
184 def delete_all_members
184 Member.delete_all(['project_id = ?', id])
185 Member.delete_all(['project_id = ?', id])
185 end
186 end
186
187
187 # Users issues can be assigned to
188 # Users issues can be assigned to
188 def assignable_users
189 def assignable_users
189 members.select {|m| m.role.assignable?}.collect {|m| m.user}.sort
190 members.select {|m| m.role.assignable?}.collect {|m| m.user}.sort
190 end
191 end
191
192
192 # Returns the mail adresses of users that should be always notified on project events
193 # Returns the mail adresses of users that should be always notified on project events
193 def recipients
194 def recipients
194 members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail}
195 members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail}
195 end
196 end
196
197
197 # Returns an array of all custom fields enabled for project issues
198 # Returns an array of all custom fields enabled for project issues
198 # (explictly associated custom fields and custom fields enabled for all projects)
199 # (explictly associated custom fields and custom fields enabled for all projects)
199 def all_issue_custom_fields
200 def all_issue_custom_fields
200 @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq
201 @all_issue_custom_fields ||= (IssueCustomField.for_all + issue_custom_fields).uniq
201 end
202 end
202
203
203 def project
204 def project
204 self
205 self
205 end
206 end
206
207
207 def <=>(project)
208 def <=>(project)
208 name.downcase <=> project.name.downcase
209 name.downcase <=> project.name.downcase
209 end
210 end
210
211
211 def to_s
212 def to_s
212 name
213 name
213 end
214 end
214
215
215 # Returns a short description of the projects (first lines)
216 # Returns a short description of the projects (first lines)
216 def short_description(length = 255)
217 def short_description(length = 255)
217 description.gsub(/^(.{#{length}}[^\n]*).*$/m, '\1').strip if description
218 description.gsub(/^(.{#{length}}[^\n]*).*$/m, '\1').strip if description
218 end
219 end
219
220
220 def allows_to?(action)
221 def allows_to?(action)
221 if action.is_a? Hash
222 if action.is_a? Hash
222 allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
223 allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
223 else
224 else
224 allowed_permissions.include? action
225 allowed_permissions.include? action
225 end
226 end
226 end
227 end
227
228
228 def module_enabled?(module_name)
229 def module_enabled?(module_name)
229 module_name = module_name.to_s
230 module_name = module_name.to_s
230 enabled_modules.detect {|m| m.name == module_name}
231 enabled_modules.detect {|m| m.name == module_name}
231 end
232 end
232
233
233 def enabled_module_names=(module_names)
234 def enabled_module_names=(module_names)
234 enabled_modules.clear
235 enabled_modules.clear
235 module_names = [] unless module_names && module_names.is_a?(Array)
236 module_names = [] unless module_names && module_names.is_a?(Array)
236 module_names.each do |name|
237 module_names.each do |name|
237 enabled_modules << EnabledModule.new(:name => name.to_s)
238 enabled_modules << EnabledModule.new(:name => name.to_s)
238 end
239 end
239 end
240 end
240
241
241 protected
242 protected
242 def validate
243 def validate
243 errors.add(parent_id, " must be a root project") if parent and parent.parent
244 errors.add(parent_id, " must be a root project") if parent and parent.parent
244 errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
245 errors.add_to_base("A project with subprojects can't be a subproject") if parent and children.size > 0
245 errors.add(:identifier, :activerecord_error_invalid) if !identifier.blank? && identifier.match(/^\d*$/)
246 errors.add(:identifier, :activerecord_error_invalid) if !identifier.blank? && identifier.match(/^\d*$/)
246 end
247 end
247
248
248 private
249 private
249 def allowed_permissions
250 def allowed_permissions
250 @allowed_permissions ||= begin
251 @allowed_permissions ||= begin
251 module_names = enabled_modules.collect {|m| m.name}
252 module_names = enabled_modules.collect {|m| m.name}
252 Redmine::AccessControl.modules_permissions(module_names).collect {|p| p.name}
253 Redmine::AccessControl.modules_permissions(module_names).collect {|p| p.name}
253 end
254 end
254 end
255 end
255
256
256 def allowed_actions
257 def allowed_actions
257 @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
258 @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
258 end
259 end
259 end
260 end
@@ -1,307 +1,315
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 @request.session[:user_id] = nil
33 end
33 end
34
34
35 def test_index
35 def test_index
36 get :index
36 get :index
37 assert_response :success
37 assert_response :success
38 assert_template 'index'
38 assert_template 'index'
39 assert_not_nil assigns(:project_tree)
39 assert_not_nil assigns(:project_tree)
40 # Root project as hash key
40 # Root project as hash key
41 assert assigns(:project_tree).keys.include?(Project.find(1))
41 assert assigns(:project_tree).keys.include?(Project.find(1))
42 # Subproject in corresponding value
42 # Subproject in corresponding value
43 assert assigns(:project_tree)[Project.find(1)].include?(Project.find(3))
43 assert assigns(:project_tree)[Project.find(1)].include?(Project.find(3))
44 end
44 end
45
45
46 def test_index_atom
47 get :index, :format => 'atom'
48 assert_response :success
49 assert_template 'common/feed.atom.rxml'
50 assert_select 'feed>title', :text => 'Redmine: Latest projects'
51 assert_select 'feed>entry', :count => Project.count(:conditions => Project.visible_by(User.current))
52 end
53
46 def test_show_by_id
54 def test_show_by_id
47 get :show, :id => 1
55 get :show, :id => 1
48 assert_response :success
56 assert_response :success
49 assert_template 'show'
57 assert_template 'show'
50 assert_not_nil assigns(:project)
58 assert_not_nil assigns(:project)
51 end
59 end
52
60
53 def test_show_by_identifier
61 def test_show_by_identifier
54 get :show, :id => 'ecookbook'
62 get :show, :id => 'ecookbook'
55 assert_response :success
63 assert_response :success
56 assert_template 'show'
64 assert_template 'show'
57 assert_not_nil assigns(:project)
65 assert_not_nil assigns(:project)
58 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
66 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
59 end
67 end
60
68
61 def test_private_subprojects_hidden
69 def test_private_subprojects_hidden
62 get :show, :id => 'ecookbook'
70 get :show, :id => 'ecookbook'
63 assert_response :success
71 assert_response :success
64 assert_template 'show'
72 assert_template 'show'
65 assert_no_tag :tag => 'a', :content => /Private child/
73 assert_no_tag :tag => 'a', :content => /Private child/
66 end
74 end
67
75
68 def test_private_subprojects_visible
76 def test_private_subprojects_visible
69 @request.session[:user_id] = 2 # manager who is a member of the private subproject
77 @request.session[:user_id] = 2 # manager who is a member of the private subproject
70 get :show, :id => 'ecookbook'
78 get :show, :id => 'ecookbook'
71 assert_response :success
79 assert_response :success
72 assert_template 'show'
80 assert_template 'show'
73 assert_tag :tag => 'a', :content => /Private child/
81 assert_tag :tag => 'a', :content => /Private child/
74 end
82 end
75
83
76 def test_settings
84 def test_settings
77 @request.session[:user_id] = 2 # manager
85 @request.session[:user_id] = 2 # manager
78 get :settings, :id => 1
86 get :settings, :id => 1
79 assert_response :success
87 assert_response :success
80 assert_template 'settings'
88 assert_template 'settings'
81 end
89 end
82
90
83 def test_edit
91 def test_edit
84 @request.session[:user_id] = 2 # manager
92 @request.session[:user_id] = 2 # manager
85 post :edit, :id => 1, :project => {:name => 'Test changed name',
93 post :edit, :id => 1, :project => {:name => 'Test changed name',
86 :issue_custom_field_ids => ['']}
94 :issue_custom_field_ids => ['']}
87 assert_redirected_to 'projects/settings/ecookbook'
95 assert_redirected_to 'projects/settings/ecookbook'
88 project = Project.find(1)
96 project = Project.find(1)
89 assert_equal 'Test changed name', project.name
97 assert_equal 'Test changed name', project.name
90 end
98 end
91
99
92 def test_get_destroy
100 def test_get_destroy
93 @request.session[:user_id] = 1 # admin
101 @request.session[:user_id] = 1 # admin
94 get :destroy, :id => 1
102 get :destroy, :id => 1
95 assert_response :success
103 assert_response :success
96 assert_template 'destroy'
104 assert_template 'destroy'
97 assert_not_nil Project.find_by_id(1)
105 assert_not_nil Project.find_by_id(1)
98 end
106 end
99
107
100 def test_post_destroy
108 def test_post_destroy
101 @request.session[:user_id] = 1 # admin
109 @request.session[:user_id] = 1 # admin
102 post :destroy, :id => 1, :confirm => 1
110 post :destroy, :id => 1, :confirm => 1
103 assert_redirected_to 'admin/projects'
111 assert_redirected_to 'admin/projects'
104 assert_nil Project.find_by_id(1)
112 assert_nil Project.find_by_id(1)
105 end
113 end
106
114
107 def test_list_files
115 def test_list_files
108 get :list_files, :id => 1
116 get :list_files, :id => 1
109 assert_response :success
117 assert_response :success
110 assert_template 'list_files'
118 assert_template 'list_files'
111 assert_not_nil assigns(:versions)
119 assert_not_nil assigns(:versions)
112 end
120 end
113
121
114 def test_changelog
122 def test_changelog
115 get :changelog, :id => 1
123 get :changelog, :id => 1
116 assert_response :success
124 assert_response :success
117 assert_template 'changelog'
125 assert_template 'changelog'
118 assert_not_nil assigns(:versions)
126 assert_not_nil assigns(:versions)
119 end
127 end
120
128
121 def test_roadmap
129 def test_roadmap
122 get :roadmap, :id => 1
130 get :roadmap, :id => 1
123 assert_response :success
131 assert_response :success
124 assert_template 'roadmap'
132 assert_template 'roadmap'
125 assert_not_nil assigns(:versions)
133 assert_not_nil assigns(:versions)
126 # Version with no date set appears
134 # Version with no date set appears
127 assert assigns(:versions).include?(Version.find(3))
135 assert assigns(:versions).include?(Version.find(3))
128 # Completed version doesn't appear
136 # Completed version doesn't appear
129 assert !assigns(:versions).include?(Version.find(1))
137 assert !assigns(:versions).include?(Version.find(1))
130 end
138 end
131
139
132 def test_roadmap_with_completed_versions
140 def test_roadmap_with_completed_versions
133 get :roadmap, :id => 1, :completed => 1
141 get :roadmap, :id => 1, :completed => 1
134 assert_response :success
142 assert_response :success
135 assert_template 'roadmap'
143 assert_template 'roadmap'
136 assert_not_nil assigns(:versions)
144 assert_not_nil assigns(:versions)
137 # Version with no date set appears
145 # Version with no date set appears
138 assert assigns(:versions).include?(Version.find(3))
146 assert assigns(:versions).include?(Version.find(3))
139 # Completed version appears
147 # Completed version appears
140 assert assigns(:versions).include?(Version.find(1))
148 assert assigns(:versions).include?(Version.find(1))
141 end
149 end
142
150
143 def test_project_activity
151 def test_project_activity
144 get :activity, :id => 1, :with_subprojects => 0
152 get :activity, :id => 1, :with_subprojects => 0
145 assert_response :success
153 assert_response :success
146 assert_template 'activity'
154 assert_template 'activity'
147 assert_not_nil assigns(:events_by_day)
155 assert_not_nil assigns(:events_by_day)
148 assert_not_nil assigns(:events)
156 assert_not_nil assigns(:events)
149
157
150 # subproject issue not included by default
158 # subproject issue not included by default
151 assert !assigns(:events).include?(Issue.find(5))
159 assert !assigns(:events).include?(Issue.find(5))
152
160
153 assert_tag :tag => "h3",
161 assert_tag :tag => "h3",
154 :content => /#{2.days.ago.to_date.day}/,
162 :content => /#{2.days.ago.to_date.day}/,
155 :sibling => { :tag => "dl",
163 :sibling => { :tag => "dl",
156 :child => { :tag => "dt",
164 :child => { :tag => "dt",
157 :attributes => { :class => /issue-edit/ },
165 :attributes => { :class => /issue-edit/ },
158 :child => { :tag => "a",
166 :child => { :tag => "a",
159 :content => /(#{IssueStatus.find(2).name})/,
167 :content => /(#{IssueStatus.find(2).name})/,
160 }
168 }
161 }
169 }
162 }
170 }
163
171
164 get :activity, :id => 1, :from => 3.days.ago.to_date
172 get :activity, :id => 1, :from => 3.days.ago.to_date
165 assert_response :success
173 assert_response :success
166 assert_template 'activity'
174 assert_template 'activity'
167 assert_not_nil assigns(:events_by_day)
175 assert_not_nil assigns(:events_by_day)
168
176
169 assert_tag :tag => "h3",
177 assert_tag :tag => "h3",
170 :content => /#{3.day.ago.to_date.day}/,
178 :content => /#{3.day.ago.to_date.day}/,
171 :sibling => { :tag => "dl",
179 :sibling => { :tag => "dl",
172 :child => { :tag => "dt",
180 :child => { :tag => "dt",
173 :attributes => { :class => /issue/ },
181 :attributes => { :class => /issue/ },
174 :child => { :tag => "a",
182 :child => { :tag => "a",
175 :content => /#{Issue.find(1).subject}/,
183 :content => /#{Issue.find(1).subject}/,
176 }
184 }
177 }
185 }
178 }
186 }
179 end
187 end
180
188
181 def test_activity_with_subprojects
189 def test_activity_with_subprojects
182 get :activity, :id => 1, :with_subprojects => 1
190 get :activity, :id => 1, :with_subprojects => 1
183 assert_response :success
191 assert_response :success
184 assert_template 'activity'
192 assert_template 'activity'
185 assert_not_nil assigns(:events)
193 assert_not_nil assigns(:events)
186
194
187 assert assigns(:events).include?(Issue.find(1))
195 assert assigns(:events).include?(Issue.find(1))
188 assert !assigns(:events).include?(Issue.find(4))
196 assert !assigns(:events).include?(Issue.find(4))
189 # subproject issue
197 # subproject issue
190 assert assigns(:events).include?(Issue.find(5))
198 assert assigns(:events).include?(Issue.find(5))
191 end
199 end
192
200
193 def test_global_activity_anonymous
201 def test_global_activity_anonymous
194 get :activity
202 get :activity
195 assert_response :success
203 assert_response :success
196 assert_template 'activity'
204 assert_template 'activity'
197 assert_not_nil assigns(:events)
205 assert_not_nil assigns(:events)
198
206
199 assert assigns(:events).include?(Issue.find(1))
207 assert assigns(:events).include?(Issue.find(1))
200 # Issue of a private project
208 # Issue of a private project
201 assert !assigns(:events).include?(Issue.find(4))
209 assert !assigns(:events).include?(Issue.find(4))
202 end
210 end
203
211
204 def test_global_activity_logged_user
212 def test_global_activity_logged_user
205 @request.session[:user_id] = 2 # manager
213 @request.session[:user_id] = 2 # manager
206 get :activity
214 get :activity
207 assert_response :success
215 assert_response :success
208 assert_template 'activity'
216 assert_template 'activity'
209 assert_not_nil assigns(:events)
217 assert_not_nil assigns(:events)
210
218
211 assert assigns(:events).include?(Issue.find(1))
219 assert assigns(:events).include?(Issue.find(1))
212 # Issue of a private project the user belongs to
220 # Issue of a private project the user belongs to
213 assert assigns(:events).include?(Issue.find(4))
221 assert assigns(:events).include?(Issue.find(4))
214 end
222 end
215
223
216
224
217 def test_global_activity_with_all_types
225 def test_global_activity_with_all_types
218 get :activity, :show_issues => 1, :show_news => 1, :show_files => 1, :show_documents => 1, :show_changesets => 1, :show_wiki_pages => 1, :show_messages => 1
226 get :activity, :show_issues => 1, :show_news => 1, :show_files => 1, :show_documents => 1, :show_changesets => 1, :show_wiki_pages => 1, :show_messages => 1
219 assert_response :success
227 assert_response :success
220 assert_template 'activity'
228 assert_template 'activity'
221 assert_not_nil assigns(:events)
229 assert_not_nil assigns(:events)
222
230
223 assert assigns(:events).include?(Issue.find(1))
231 assert assigns(:events).include?(Issue.find(1))
224 assert !assigns(:events).include?(Issue.find(4))
232 assert !assigns(:events).include?(Issue.find(4))
225 assert assigns(:events).include?(Message.find(5))
233 assert assigns(:events).include?(Message.find(5))
226 end
234 end
227
235
228 def test_calendar
236 def test_calendar
229 get :calendar, :id => 1
237 get :calendar, :id => 1
230 assert_response :success
238 assert_response :success
231 assert_template 'calendar'
239 assert_template 'calendar'
232 assert_not_nil assigns(:calendar)
240 assert_not_nil assigns(:calendar)
233 end
241 end
234
242
235 def test_calendar_with_subprojects_should_not_show_private_subprojects
243 def test_calendar_with_subprojects_should_not_show_private_subprojects
236 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
244 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
237 assert_response :success
245 assert_response :success
238 assert_template 'calendar'
246 assert_template 'calendar'
239 assert_not_nil assigns(:calendar)
247 assert_not_nil assigns(:calendar)
240 assert_no_tag :tag => 'a', :content => /#6/
248 assert_no_tag :tag => 'a', :content => /#6/
241 end
249 end
242
250
243 def test_calendar_with_subprojects_should_show_private_subprojects
251 def test_calendar_with_subprojects_should_show_private_subprojects
244 @request.session[:user_id] = 2
252 @request.session[:user_id] = 2
245 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
253 get :calendar, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
246 assert_response :success
254 assert_response :success
247 assert_template 'calendar'
255 assert_template 'calendar'
248 assert_not_nil assigns(:calendar)
256 assert_not_nil assigns(:calendar)
249 assert_tag :tag => 'a', :content => /#6/
257 assert_tag :tag => 'a', :content => /#6/
250 end
258 end
251
259
252 def test_gantt
260 def test_gantt
253 get :gantt, :id => 1
261 get :gantt, :id => 1
254 assert_response :success
262 assert_response :success
255 assert_template 'gantt.rhtml'
263 assert_template 'gantt.rhtml'
256 events = assigns(:events)
264 events = assigns(:events)
257 assert_not_nil events
265 assert_not_nil events
258 # Issue with start and due dates
266 # Issue with start and due dates
259 i = Issue.find(1)
267 i = Issue.find(1)
260 assert_not_nil i.due_date
268 assert_not_nil i.due_date
261 assert events.include?(Issue.find(1))
269 assert events.include?(Issue.find(1))
262 # Issue with without due date but targeted to a version with date
270 # Issue with without due date but targeted to a version with date
263 i = Issue.find(2)
271 i = Issue.find(2)
264 assert_nil i.due_date
272 assert_nil i.due_date
265 assert events.include?(i)
273 assert events.include?(i)
266 end
274 end
267
275
268 def test_gantt_with_subprojects_should_not_show_private_subprojects
276 def test_gantt_with_subprojects_should_not_show_private_subprojects
269 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
277 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
270 assert_response :success
278 assert_response :success
271 assert_template 'gantt.rhtml'
279 assert_template 'gantt.rhtml'
272 assert_not_nil assigns(:events)
280 assert_not_nil assigns(:events)
273 assert_no_tag :tag => 'a', :content => /#6/
281 assert_no_tag :tag => 'a', :content => /#6/
274 end
282 end
275
283
276 def test_gantt_with_subprojects_should_show_private_subprojects
284 def test_gantt_with_subprojects_should_show_private_subprojects
277 @request.session[:user_id] = 2
285 @request.session[:user_id] = 2
278 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
286 get :gantt, :id => 1, :with_subprojects => 1, :tracker_ids => [1, 2]
279 assert_response :success
287 assert_response :success
280 assert_template 'gantt.rhtml'
288 assert_template 'gantt.rhtml'
281 assert_not_nil assigns(:events)
289 assert_not_nil assigns(:events)
282 assert_tag :tag => 'a', :content => /#6/
290 assert_tag :tag => 'a', :content => /#6/
283 end
291 end
284
292
285 def test_gantt_export_to_pdf
293 def test_gantt_export_to_pdf
286 get :gantt, :id => 1, :format => 'pdf'
294 get :gantt, :id => 1, :format => 'pdf'
287 assert_response :success
295 assert_response :success
288 assert_template 'gantt.rfpdf'
296 assert_template 'gantt.rfpdf'
289 assert_equal 'application/pdf', @response.content_type
297 assert_equal 'application/pdf', @response.content_type
290 assert_not_nil assigns(:events)
298 assert_not_nil assigns(:events)
291 end
299 end
292
300
293 def test_archive
301 def test_archive
294 @request.session[:user_id] = 1 # admin
302 @request.session[:user_id] = 1 # admin
295 post :archive, :id => 1
303 post :archive, :id => 1
296 assert_redirected_to 'admin/projects'
304 assert_redirected_to 'admin/projects'
297 assert !Project.find(1).active?
305 assert !Project.find(1).active?
298 end
306 end
299
307
300 def test_unarchive
308 def test_unarchive
301 @request.session[:user_id] = 1 # admin
309 @request.session[:user_id] = 1 # admin
302 Project.find(1).archive
310 Project.find(1).archive
303 post :unarchive, :id => 1
311 post :unarchive, :id => 1
304 assert_redirected_to 'admin/projects'
312 assert_redirected_to 'admin/projects'
305 assert Project.find(1).active?
313 assert Project.find(1).active?
306 end
314 end
307 end
315 end
@@ -1,75 +1,76
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 module Redmine
18 module Redmine
19 module Acts
19 module Acts
20 module Event
20 module Event
21 def self.included(base)
21 def self.included(base)
22 base.extend ClassMethods
22 base.extend ClassMethods
23 end
23 end
24
24
25 module ClassMethods
25 module ClassMethods
26 def acts_as_event(options = {})
26 def acts_as_event(options = {})
27 return if self.included_modules.include?(Redmine::Acts::Event::InstanceMethods)
27 return if self.included_modules.include?(Redmine::Acts::Event::InstanceMethods)
28 options[:datetime] ||= :created_on
28 default_options = { :datetime => :created_on,
29 options[:title] ||= :title
29 :title => :title,
30 options[:description] ||= :description
30 :description => :description,
31 options[:author] ||= :author
31 :author => :author,
32 options[:url] ||= {:controller => 'welcome'}
32 :url => {:controller => 'welcome'},
33 options[:type] ||= self.name.underscore.dasherize
33 :type => self.name.underscore.dasherize }
34
34 cattr_accessor :event_options
35 cattr_accessor :event_options
35 self.event_options = options
36 self.event_options = default_options.merge(options)
36 send :include, Redmine::Acts::Event::InstanceMethods
37 send :include, Redmine::Acts::Event::InstanceMethods
37 end
38 end
38 end
39 end
39
40
40 module InstanceMethods
41 module InstanceMethods
41 def self.included(base)
42 def self.included(base)
42 base.extend ClassMethods
43 base.extend ClassMethods
43 end
44 end
44
45
45 %w(datetime title description author type).each do |attr|
46 %w(datetime title description author type).each do |attr|
46 src = <<-END_SRC
47 src = <<-END_SRC
47 def event_#{attr}
48 def event_#{attr}
48 option = event_options[:#{attr}]
49 option = event_options[:#{attr}]
49 if option.is_a?(Proc)
50 if option.is_a?(Proc)
50 option.call(self)
51 option.call(self)
51 elsif option.is_a?(Symbol)
52 elsif option.is_a?(Symbol)
52 send(option)
53 send(option)
53 else
54 else
54 option
55 option
55 end
56 end
56 end
57 end
57 END_SRC
58 END_SRC
58 class_eval src, __FILE__, __LINE__
59 class_eval src, __FILE__, __LINE__
59 end
60 end
60
61
61 def event_date
62 def event_date
62 event_datetime.to_date
63 event_datetime.to_date
63 end
64 end
64
65
65 def event_url(options = {})
66 def event_url(options = {})
66 option = event_options[:url]
67 option = event_options[:url]
67 (option.is_a?(Proc) ? option.call(self) : send(option)).merge(options)
68 (option.is_a?(Proc) ? option.call(self) : send(option)).merge(options)
68 end
69 end
69
70
70 module ClassMethods
71 module ClassMethods
71 end
72 end
72 end
73 end
73 end
74 end
74 end
75 end
75 end
76 end
General Comments 0
You need to be logged in to leave comments. Login now