##// END OF EJS Templates
Ability to add a new version from the issue form (#4315)....
Jean-Philippe Lang -
r3012:a2b4bb0dfb93
parent child
Show More
@@ -1,406 +1,425
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 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 ProjectsController < ApplicationController
18 class ProjectsController < ApplicationController
19 menu_item :overview
19 menu_item :overview
20 menu_item :activity, :only => :activity
20 menu_item :activity, :only => :activity
21 menu_item :roadmap, :only => :roadmap
21 menu_item :roadmap, :only => :roadmap
22 menu_item :files, :only => [:list_files, :add_file]
22 menu_item :files, :only => [:list_files, :add_file]
23 menu_item :settings, :only => :settings
23 menu_item :settings, :only => :settings
24 menu_item :issues, :only => [:changelog]
24 menu_item :issues, :only => [:changelog]
25
25
26 before_filter :find_project, :except => [ :index, :list, :add, :copy, :activity ]
26 before_filter :find_project, :except => [ :index, :list, :add, :copy, :activity ]
27 before_filter :find_optional_project, :only => :activity
27 before_filter :find_optional_project, :only => :activity
28 before_filter :authorize, :except => [ :index, :list, :add, :copy, :archive, :unarchive, :destroy, :activity ]
28 before_filter :authorize, :except => [ :index, :list, :add, :copy, :archive, :unarchive, :destroy, :activity ]
29 before_filter :authorize_global, :only => :add
29 before_filter :authorize_global, :only => :add
30 before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
30 before_filter :require_admin, :only => [ :copy, :archive, :unarchive, :destroy ]
31 accept_key_auth :activity
31 accept_key_auth :activity
32
32
33 after_filter :only => [:add, :edit, :archive, :unarchive, :destroy] do |controller|
33 after_filter :only => [:add, :edit, :archive, :unarchive, :destroy] do |controller|
34 if controller.request.post?
34 if controller.request.post?
35 controller.send :expire_action, :controller => 'welcome', :action => 'robots.txt'
35 controller.send :expire_action, :controller => 'welcome', :action => 'robots.txt'
36 end
36 end
37 end
37 end
38
38
39 helper :sort
39 helper :sort
40 include SortHelper
40 include SortHelper
41 helper :custom_fields
41 helper :custom_fields
42 include CustomFieldsHelper
42 include CustomFieldsHelper
43 helper :issues
43 helper :issues
44 helper IssuesHelper
44 helper IssuesHelper
45 helper :queries
45 helper :queries
46 include QueriesHelper
46 include QueriesHelper
47 helper :repositories
47 helper :repositories
48 include RepositoriesHelper
48 include RepositoriesHelper
49 include ProjectsHelper
49 include ProjectsHelper
50
50
51 # Lists visible projects
51 # Lists visible projects
52 def index
52 def index
53 respond_to do |format|
53 respond_to do |format|
54 format.html {
54 format.html {
55 @projects = Project.visible.find(:all, :order => 'lft')
55 @projects = Project.visible.find(:all, :order => 'lft')
56 }
56 }
57 format.atom {
57 format.atom {
58 projects = Project.visible.find(:all, :order => 'created_on DESC',
58 projects = Project.visible.find(:all, :order => 'created_on DESC',
59 :limit => Setting.feeds_limit.to_i)
59 :limit => Setting.feeds_limit.to_i)
60 render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
60 render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
61 }
61 }
62 end
62 end
63 end
63 end
64
64
65 # Add a new project
65 # Add a new project
66 def add
66 def add
67 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
67 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
68 @trackers = Tracker.all
68 @trackers = Tracker.all
69 @project = Project.new(params[:project])
69 @project = Project.new(params[:project])
70 if request.get?
70 if request.get?
71 @project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
71 @project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
72 @project.trackers = Tracker.all
72 @project.trackers = Tracker.all
73 @project.is_public = Setting.default_projects_public?
73 @project.is_public = Setting.default_projects_public?
74 @project.enabled_module_names = Setting.default_projects_modules
74 @project.enabled_module_names = Setting.default_projects_modules
75 else
75 else
76 @project.enabled_module_names = params[:enabled_modules]
76 @project.enabled_module_names = params[:enabled_modules]
77 if @project.save
77 if @project.save
78 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
78 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
79 # Add current user as a project member if he is not admin
79 # Add current user as a project member if he is not admin
80 unless User.current.admin?
80 unless User.current.admin?
81 r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
81 r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
82 m = Member.new(:user => User.current, :roles => [r])
82 m = Member.new(:user => User.current, :roles => [r])
83 @project.members << m
83 @project.members << m
84 end
84 end
85 flash[:notice] = l(:notice_successful_create)
85 flash[:notice] = l(:notice_successful_create)
86 redirect_to :controller => 'projects', :action => 'settings', :id => @project
86 redirect_to :controller => 'projects', :action => 'settings', :id => @project
87 end
87 end
88 end
88 end
89 end
89 end
90
90
91 def copy
91 def copy
92 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
92 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
93 @trackers = Tracker.all
93 @trackers = Tracker.all
94 @root_projects = Project.find(:all,
94 @root_projects = Project.find(:all,
95 :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
95 :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
96 :order => 'name')
96 :order => 'name')
97 @source_project = Project.find(params[:id])
97 @source_project = Project.find(params[:id])
98 if request.get?
98 if request.get?
99 @project = Project.copy_from(@source_project)
99 @project = Project.copy_from(@source_project)
100 if @project
100 if @project
101 @project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
101 @project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
102 else
102 else
103 redirect_to :controller => 'admin', :action => 'projects'
103 redirect_to :controller => 'admin', :action => 'projects'
104 end
104 end
105 else
105 else
106 @project = Project.new(params[:project])
106 @project = Project.new(params[:project])
107 @project.enabled_module_names = params[:enabled_modules]
107 @project.enabled_module_names = params[:enabled_modules]
108 if @project.copy(@source_project, :only => params[:only])
108 if @project.copy(@source_project, :only => params[:only])
109 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
109 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
110 flash[:notice] = l(:notice_successful_create)
110 flash[:notice] = l(:notice_successful_create)
111 redirect_to :controller => 'admin', :action => 'projects'
111 redirect_to :controller => 'admin', :action => 'projects'
112 end
112 end
113 end
113 end
114 rescue ActiveRecord::RecordNotFound
114 rescue ActiveRecord::RecordNotFound
115 redirect_to :controller => 'admin', :action => 'projects'
115 redirect_to :controller => 'admin', :action => 'projects'
116 end
116 end
117
117
118 # Show @project
118 # Show @project
119 def show
119 def show
120 if params[:jump]
120 if params[:jump]
121 # try to redirect to the requested menu item
121 # try to redirect to the requested menu item
122 redirect_to_project_menu_item(@project, params[:jump]) && return
122 redirect_to_project_menu_item(@project, params[:jump]) && return
123 end
123 end
124
124
125 @users_by_role = @project.users_by_role
125 @users_by_role = @project.users_by_role
126 @subprojects = @project.children.visible
126 @subprojects = @project.children.visible
127 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
127 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
128 @trackers = @project.rolled_up_trackers
128 @trackers = @project.rolled_up_trackers
129
129
130 cond = @project.project_condition(Setting.display_subprojects_issues?)
130 cond = @project.project_condition(Setting.display_subprojects_issues?)
131
131
132 @open_issues_by_tracker = Issue.visible.count(:group => :tracker,
132 @open_issues_by_tracker = Issue.visible.count(:group => :tracker,
133 :include => [:project, :status, :tracker],
133 :include => [:project, :status, :tracker],
134 :conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
134 :conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
135 @total_issues_by_tracker = Issue.visible.count(:group => :tracker,
135 @total_issues_by_tracker = Issue.visible.count(:group => :tracker,
136 :include => [:project, :status, :tracker],
136 :include => [:project, :status, :tracker],
137 :conditions => cond)
137 :conditions => cond)
138
138
139 TimeEntry.visible_by(User.current) do
139 TimeEntry.visible_by(User.current) do
140 @total_hours = TimeEntry.sum(:hours,
140 @total_hours = TimeEntry.sum(:hours,
141 :include => :project,
141 :include => :project,
142 :conditions => cond).to_f
142 :conditions => cond).to_f
143 end
143 end
144 @key = User.current.rss_key
144 @key = User.current.rss_key
145 end
145 end
146
146
147 def settings
147 def settings
148 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
148 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
149 @issue_category ||= IssueCategory.new
149 @issue_category ||= IssueCategory.new
150 @member ||= @project.members.new
150 @member ||= @project.members.new
151 @trackers = Tracker.all
151 @trackers = Tracker.all
152 @repository ||= @project.repository
152 @repository ||= @project.repository
153 @wiki ||= @project.wiki
153 @wiki ||= @project.wiki
154 end
154 end
155
155
156 # Edit @project
156 # Edit @project
157 def edit
157 def edit
158 if request.post?
158 if request.post?
159 @project.attributes = params[:project]
159 @project.attributes = params[:project]
160 if @project.save
160 if @project.save
161 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
161 @project.set_allowed_parent!(params[:project]['parent_id']) if params[:project].has_key?('parent_id')
162 flash[:notice] = l(:notice_successful_update)
162 flash[:notice] = l(:notice_successful_update)
163 redirect_to :action => 'settings', :id => @project
163 redirect_to :action => 'settings', :id => @project
164 else
164 else
165 settings
165 settings
166 render :action => 'settings'
166 render :action => 'settings'
167 end
167 end
168 end
168 end
169 end
169 end
170
170
171 def modules
171 def modules
172 @project.enabled_module_names = params[:enabled_modules]
172 @project.enabled_module_names = params[:enabled_modules]
173 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
173 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
174 end
174 end
175
175
176 def archive
176 def archive
177 if request.post?
177 if request.post?
178 unless @project.archive
178 unless @project.archive
179 flash[:error] = l(:error_can_not_archive_project)
179 flash[:error] = l(:error_can_not_archive_project)
180 end
180 end
181 end
181 end
182 redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
182 redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
183 end
183 end
184
184
185 def unarchive
185 def unarchive
186 @project.unarchive if request.post? && !@project.active?
186 @project.unarchive if request.post? && !@project.active?
187 redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
187 redirect_to(url_for(:controller => 'admin', :action => 'projects', :status => params[:status]))
188 end
188 end
189
189
190 # Delete @project
190 # Delete @project
191 def destroy
191 def destroy
192 @project_to_destroy = @project
192 @project_to_destroy = @project
193 if request.post? and params[:confirm]
193 if request.post? and params[:confirm]
194 @project_to_destroy.destroy
194 @project_to_destroy.destroy
195 redirect_to :controller => 'admin', :action => 'projects'
195 redirect_to :controller => 'admin', :action => 'projects'
196 end
196 end
197 # hide project in layout
197 # hide project in layout
198 @project = nil
198 @project = nil
199 end
199 end
200
200
201 # Add a new issue category to @project
201 # Add a new issue category to @project
202 def add_issue_category
202 def add_issue_category
203 @category = @project.issue_categories.build(params[:category])
203 @category = @project.issue_categories.build(params[:category])
204 if request.post?
204 if request.post?
205 if @category.save
205 if @category.save
206 respond_to do |format|
206 respond_to do |format|
207 format.html do
207 format.html do
208 flash[:notice] = l(:notice_successful_create)
208 flash[:notice] = l(:notice_successful_create)
209 redirect_to :action => 'settings', :tab => 'categories', :id => @project
209 redirect_to :action => 'settings', :tab => 'categories', :id => @project
210 end
210 end
211 format.js do
211 format.js do
212 # IE doesn't support the replace_html rjs method for select box options
212 # IE doesn't support the replace_html rjs method for select box options
213 render(:update) {|page| page.replace "issue_category_id",
213 render(:update) {|page| page.replace "issue_category_id",
214 content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
214 content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
215 }
215 }
216 end
216 end
217 end
217 end
218 else
218 else
219 respond_to do |format|
219 respond_to do |format|
220 format.html
220 format.html
221 format.js do
221 format.js do
222 render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
222 render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) }
223 end
223 end
224 end
224 end
225 end
225 end
226 end
226 end
227 end
227 end
228
228
229 # Add a new version to @project
229 # Add a new version to @project
230 def add_version
230 def add_version
231 @version = @project.versions.build
231 @version = @project.versions.build
232 if params[:version]
232 if params[:version]
233 attributes = params[:version].dup
233 attributes = params[:version].dup
234 attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
234 attributes.delete('sharing') unless attributes.nil? || @version.allowed_sharings.include?(attributes['sharing'])
235 @version.attributes = attributes
235 @version.attributes = attributes
236 end
236 end
237 if request.post? and @version.save
237 if request.post?
238 flash[:notice] = l(:notice_successful_create)
238 if @version.save
239 redirect_to :action => 'settings', :tab => 'versions', :id => @project
239 respond_to do |format|
240 format.html do
241 flash[:notice] = l(:notice_successful_create)
242 redirect_to :action => 'settings', :tab => 'versions', :id => @project
243 end
244 format.js do
245 # IE doesn't support the replace_html rjs method for select box options
246 render(:update) {|page| page.replace "issue_fixed_version_id",
247 content_tag('select', '<option></option>' + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]')
248 }
249 end
250 end
251 else
252 respond_to do |format|
253 format.html
254 format.js do
255 render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) }
256 end
257 end
258 end
240 end
259 end
241 end
260 end
242
261
243 def add_file
262 def add_file
244 if request.post?
263 if request.post?
245 container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
264 container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
246 attachments = attach_files(container, params[:attachments])
265 attachments = attach_files(container, params[:attachments])
247 if !attachments.empty? && Setting.notified_events.include?('file_added')
266 if !attachments.empty? && Setting.notified_events.include?('file_added')
248 Mailer.deliver_attachments_added(attachments)
267 Mailer.deliver_attachments_added(attachments)
249 end
268 end
250 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
269 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
251 return
270 return
252 end
271 end
253 @versions = @project.versions.sort
272 @versions = @project.versions.sort
254 end
273 end
255
274
256 def save_activities
275 def save_activities
257 if request.post? && params[:enumerations]
276 if request.post? && params[:enumerations]
258 Project.transaction do
277 Project.transaction do
259 params[:enumerations].each do |id, activity|
278 params[:enumerations].each do |id, activity|
260 @project.update_or_create_time_entry_activity(id, activity)
279 @project.update_or_create_time_entry_activity(id, activity)
261 end
280 end
262 end
281 end
263 end
282 end
264
283
265 redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
284 redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
266 end
285 end
267
286
268 def reset_activities
287 def reset_activities
269 @project.time_entry_activities.each do |time_entry_activity|
288 @project.time_entry_activities.each do |time_entry_activity|
270 time_entry_activity.destroy(time_entry_activity.parent)
289 time_entry_activity.destroy(time_entry_activity.parent)
271 end
290 end
272 redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
291 redirect_to :controller => 'projects', :action => 'settings', :tab => 'activities', :id => @project
273 end
292 end
274
293
275 def list_files
294 def list_files
276 sort_init 'filename', 'asc'
295 sort_init 'filename', 'asc'
277 sort_update 'filename' => "#{Attachment.table_name}.filename",
296 sort_update 'filename' => "#{Attachment.table_name}.filename",
278 'created_on' => "#{Attachment.table_name}.created_on",
297 'created_on' => "#{Attachment.table_name}.created_on",
279 'size' => "#{Attachment.table_name}.filesize",
298 'size' => "#{Attachment.table_name}.filesize",
280 'downloads' => "#{Attachment.table_name}.downloads"
299 'downloads' => "#{Attachment.table_name}.downloads"
281
300
282 @containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
301 @containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
283 @containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
302 @containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
284 render :layout => !request.xhr?
303 render :layout => !request.xhr?
285 end
304 end
286
305
287 # Show changelog for @project
306 # Show changelog for @project
288 def changelog
307 def changelog
289 @trackers = @project.trackers.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
308 @trackers = @project.trackers.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
290 retrieve_selected_tracker_ids(@trackers)
309 retrieve_selected_tracker_ids(@trackers)
291 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
310 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
292 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
311 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
293
312
294 @versions = @project.shared_versions.sort
313 @versions = @project.shared_versions.sort
295
314
296 @issues_by_version = {}
315 @issues_by_version = {}
297 unless @selected_tracker_ids.empty?
316 unless @selected_tracker_ids.empty?
298 @versions.each do |version|
317 @versions.each do |version|
299 conditions = {:tracker_id => @selected_tracker_ids, "#{IssueStatus.table_name}.is_closed" => true}
318 conditions = {:tracker_id => @selected_tracker_ids, "#{IssueStatus.table_name}.is_closed" => true}
300 if !@project.versions.include?(version)
319 if !@project.versions.include?(version)
301 conditions.merge!(:project_id => project_ids)
320 conditions.merge!(:project_id => project_ids)
302 end
321 end
303 issues = version.fixed_issues.visible.find(:all,
322 issues = version.fixed_issues.visible.find(:all,
304 :include => [:status, :tracker, :priority],
323 :include => [:status, :tracker, :priority],
305 :conditions => conditions,
324 :conditions => conditions,
306 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
325 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
307 @issues_by_version[version] = issues
326 @issues_by_version[version] = issues
308 end
327 end
309 end
328 end
310 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].empty?}
329 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].empty?}
311 end
330 end
312
331
313 def roadmap
332 def roadmap
314 @trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
333 @trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true], :order => 'position')
315 retrieve_selected_tracker_ids(@trackers)
334 retrieve_selected_tracker_ids(@trackers)
316 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
335 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
317 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
336 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
318
337
319 @versions = @project.shared_versions.sort
338 @versions = @project.shared_versions.sort
320 @versions.reject! {|version| version.closed? || version.completed? } unless params[:completed]
339 @versions.reject! {|version| version.closed? || version.completed? } unless params[:completed]
321
340
322 @issues_by_version = {}
341 @issues_by_version = {}
323 unless @selected_tracker_ids.empty?
342 unless @selected_tracker_ids.empty?
324 @versions.each do |version|
343 @versions.each do |version|
325 conditions = {:tracker_id => @selected_tracker_ids}
344 conditions = {:tracker_id => @selected_tracker_ids}
326 if !@project.versions.include?(version)
345 if !@project.versions.include?(version)
327 conditions.merge!(:project_id => project_ids)
346 conditions.merge!(:project_id => project_ids)
328 end
347 end
329 issues = version.fixed_issues.visible.find(:all,
348 issues = version.fixed_issues.visible.find(:all,
330 :include => [:status, :tracker, :priority],
349 :include => [:status, :tracker, :priority],
331 :conditions => conditions,
350 :conditions => conditions,
332 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
351 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
333 @issues_by_version[version] = issues
352 @issues_by_version[version] = issues
334 end
353 end
335 end
354 end
336 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].empty?}
355 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].empty?}
337 end
356 end
338
357
339 def activity
358 def activity
340 @days = Setting.activity_days_default.to_i
359 @days = Setting.activity_days_default.to_i
341
360
342 if params[:from]
361 if params[:from]
343 begin; @date_to = params[:from].to_date + 1; rescue; end
362 begin; @date_to = params[:from].to_date + 1; rescue; end
344 end
363 end
345
364
346 @date_to ||= Date.today + 1
365 @date_to ||= Date.today + 1
347 @date_from = @date_to - @days
366 @date_from = @date_to - @days
348 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
367 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
349 @author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id]))
368 @author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id]))
350
369
351 @activity = Redmine::Activity::Fetcher.new(User.current, :project => @project,
370 @activity = Redmine::Activity::Fetcher.new(User.current, :project => @project,
352 :with_subprojects => @with_subprojects,
371 :with_subprojects => @with_subprojects,
353 :author => @author)
372 :author => @author)
354 @activity.scope_select {|t| !params["show_#{t}"].nil?}
373 @activity.scope_select {|t| !params["show_#{t}"].nil?}
355 @activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
374 @activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
356
375
357 events = @activity.events(@date_from, @date_to)
376 events = @activity.events(@date_from, @date_to)
358
377
359 if events.empty? || stale?(:etag => [events.first, User.current])
378 if events.empty? || stale?(:etag => [events.first, User.current])
360 respond_to do |format|
379 respond_to do |format|
361 format.html {
380 format.html {
362 @events_by_day = events.group_by(&:event_date)
381 @events_by_day = events.group_by(&:event_date)
363 render :layout => false if request.xhr?
382 render :layout => false if request.xhr?
364 }
383 }
365 format.atom {
384 format.atom {
366 title = l(:label_activity)
385 title = l(:label_activity)
367 if @author
386 if @author
368 title = @author.name
387 title = @author.name
369 elsif @activity.scope.size == 1
388 elsif @activity.scope.size == 1
370 title = l("label_#{@activity.scope.first.singularize}_plural")
389 title = l("label_#{@activity.scope.first.singularize}_plural")
371 end
390 end
372 render_feed(events, :title => "#{@project || Setting.app_title}: #{title}")
391 render_feed(events, :title => "#{@project || Setting.app_title}: #{title}")
373 }
392 }
374 end
393 end
375 end
394 end
376
395
377 rescue ActiveRecord::RecordNotFound
396 rescue ActiveRecord::RecordNotFound
378 render_404
397 render_404
379 end
398 end
380
399
381 private
400 private
382 # Find project of id params[:id]
401 # Find project of id params[:id]
383 # if not found, redirect to project list
402 # if not found, redirect to project list
384 # Used as a before_filter
403 # Used as a before_filter
385 def find_project
404 def find_project
386 @project = Project.find(params[:id])
405 @project = Project.find(params[:id])
387 rescue ActiveRecord::RecordNotFound
406 rescue ActiveRecord::RecordNotFound
388 render_404
407 render_404
389 end
408 end
390
409
391 def find_optional_project
410 def find_optional_project
392 return true unless params[:id]
411 return true unless params[:id]
393 @project = Project.find(params[:id])
412 @project = Project.find(params[:id])
394 authorize
413 authorize
395 rescue ActiveRecord::RecordNotFound
414 rescue ActiveRecord::RecordNotFound
396 render_404
415 render_404
397 end
416 end
398
417
399 def retrieve_selected_tracker_ids(selectable_trackers)
418 def retrieve_selected_tracker_ids(selectable_trackers)
400 if ids = params[:tracker_ids]
419 if ids = params[:tracker_ids]
401 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
420 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
402 else
421 else
403 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
422 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
404 end
423 end
405 end
424 end
406 end
425 end
@@ -1,36 +1,43
1 <% fields_for :issue, @issue, :builder => TabularFormBuilder do |f| %>
1 <% fields_for :issue, @issue, :builder => TabularFormBuilder do |f| %>
2
2
3 <div class="splitcontentleft">
3 <div class="splitcontentleft">
4 <% if @issue.new_record? || @allowed_statuses.any? %>
4 <% if @issue.new_record? || @allowed_statuses.any? %>
5 <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
5 <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
6 <% else %>
6 <% else %>
7 <p><label><%= l(:field_status) %></label> <%= @issue.status.name %></p>
7 <p><label><%= l(:field_status) %></label> <%= @issue.status.name %></p>
8 <% end %>
8 <% end %>
9
9
10 <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
10 <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), :required => true %></p>
11 <p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
11 <p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p>
12 <% unless @project.issue_categories.empty? %>
12 <% unless @project.issue_categories.empty? %>
13 <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
13 <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
14 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
14 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
15 l(:label_issue_category_new),
15 l(:label_issue_category_new),
16 'category[name]',
16 'category[name]',
17 {:controller => 'projects', :action => 'add_issue_category', :id => @project},
17 {:controller => 'projects', :action => 'add_issue_category', :id => @project},
18 :title => l(:label_issue_category_new),
18 :title => l(:label_issue_category_new),
19 :tabindex => 199) if authorize_for('projects', 'add_issue_category') %></p>
19 :tabindex => 199) if authorize_for('projects', 'add_issue_category') %></p>
20 <% end %>
20 <% end %>
21 <% unless @issue.assignable_versions.empty? %>
21 <% unless @issue.assignable_versions.empty? %>
22 <p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true %></p>
22 <p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true %>
23 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
24 l(:label_version_new),
25 'version[name]',
26 {:controller => 'projects', :action => 'add_version', :id => @project},
27 :title => l(:label_version_new),
28 :tabindex => 200) if authorize_for('projects', 'add_version') %>
29 </p>
23 <% end %>
30 <% end %>
24 </div>
31 </div>
25
32
26 <div class="splitcontentright">
33 <div class="splitcontentright">
27 <p><%= f.text_field :start_date, :size => 10 %><%= calendar_for('issue_start_date') %></p>
34 <p><%= f.text_field :start_date, :size => 10 %><%= calendar_for('issue_start_date') %></p>
28 <p><%= f.text_field :due_date, :size => 10 %><%= calendar_for('issue_due_date') %></p>
35 <p><%= f.text_field :due_date, :size => 10 %><%= calendar_for('issue_due_date') %></p>
29 <p><%= f.text_field :estimated_hours, :size => 3 %> <%= l(:field_hours) %></p>
36 <p><%= f.text_field :estimated_hours, :size => 3 %> <%= l(:field_hours) %></p>
30 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
37 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
31 </div>
38 </div>
32
39
33 <div style="clear:both;"> </div>
40 <div style="clear:both;"> </div>
34 <%= render :partial => 'form_custom_fields' %>
41 <%= render :partial => 'form_custom_fields' %>
35
42
36 <% end %>
43 <% end %>
@@ -1,800 +1,823
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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 < ActionController::TestCase
24 class ProjectsControllerTest < ActionController::TestCase
25 fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details,
25 fixtures :projects, :versions, :users, :roles, :members, :member_roles, :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 :attachments, :custom_fields, :custom_values, :time_entries
27 :attachments, :custom_fields, :custom_values, :time_entries
28
28
29 def setup
29 def setup
30 @controller = ProjectsController.new
30 @controller = ProjectsController.new
31 @request = ActionController::TestRequest.new
31 @request = ActionController::TestRequest.new
32 @response = ActionController::TestResponse.new
32 @response = ActionController::TestResponse.new
33 @request.session[:user_id] = nil
33 @request.session[:user_id] = nil
34 Setting.default_language = 'en'
34 Setting.default_language = 'en'
35 end
35 end
36
36
37 def test_index_routing
37 def test_index_routing
38 assert_routing(
38 assert_routing(
39 {:method => :get, :path => '/projects'},
39 {:method => :get, :path => '/projects'},
40 :controller => 'projects', :action => 'index'
40 :controller => 'projects', :action => 'index'
41 )
41 )
42 end
42 end
43
43
44 def test_index
44 def test_index
45 get :index
45 get :index
46 assert_response :success
46 assert_response :success
47 assert_template 'index'
47 assert_template 'index'
48 assert_not_nil assigns(:projects)
48 assert_not_nil assigns(:projects)
49
49
50 assert_tag :ul, :child => {:tag => 'li',
50 assert_tag :ul, :child => {:tag => 'li',
51 :descendant => {:tag => 'a', :content => 'eCookbook'},
51 :descendant => {:tag => 'a', :content => 'eCookbook'},
52 :child => { :tag => 'ul',
52 :child => { :tag => 'ul',
53 :descendant => { :tag => 'a',
53 :descendant => { :tag => 'a',
54 :content => 'Child of private child'
54 :content => 'Child of private child'
55 }
55 }
56 }
56 }
57 }
57 }
58
58
59 assert_no_tag :a, :content => /Private child of eCookbook/
59 assert_no_tag :a, :content => /Private child of eCookbook/
60 end
60 end
61
61
62 def test_index_atom_routing
62 def test_index_atom_routing
63 assert_routing(
63 assert_routing(
64 {:method => :get, :path => '/projects.atom'},
64 {:method => :get, :path => '/projects.atom'},
65 :controller => 'projects', :action => 'index', :format => 'atom'
65 :controller => 'projects', :action => 'index', :format => 'atom'
66 )
66 )
67 end
67 end
68
68
69 def test_index_atom
69 def test_index_atom
70 get :index, :format => 'atom'
70 get :index, :format => 'atom'
71 assert_response :success
71 assert_response :success
72 assert_template 'common/feed.atom.rxml'
72 assert_template 'common/feed.atom.rxml'
73 assert_select 'feed>title', :text => 'Redmine: Latest projects'
73 assert_select 'feed>title', :text => 'Redmine: Latest projects'
74 assert_select 'feed>entry', :count => Project.count(:conditions => Project.visible_by(User.current))
74 assert_select 'feed>entry', :count => Project.count(:conditions => Project.visible_by(User.current))
75 end
75 end
76
76
77 def test_add_routing
77 def test_add_routing
78 assert_routing(
78 assert_routing(
79 {:method => :get, :path => '/projects/new'},
79 {:method => :get, :path => '/projects/new'},
80 :controller => 'projects', :action => 'add'
80 :controller => 'projects', :action => 'add'
81 )
81 )
82 assert_recognizes(
82 assert_recognizes(
83 {:controller => 'projects', :action => 'add'},
83 {:controller => 'projects', :action => 'add'},
84 {:method => :post, :path => '/projects/new'}
84 {:method => :post, :path => '/projects/new'}
85 )
85 )
86 assert_recognizes(
86 assert_recognizes(
87 {:controller => 'projects', :action => 'add'},
87 {:controller => 'projects', :action => 'add'},
88 {:method => :post, :path => '/projects'}
88 {:method => :post, :path => '/projects'}
89 )
89 )
90 end
90 end
91
91
92 def test_get_add
92 def test_get_add
93 @request.session[:user_id] = 1
93 @request.session[:user_id] = 1
94 get :add
94 get :add
95 assert_response :success
95 assert_response :success
96 assert_template 'add'
96 assert_template 'add'
97 end
97 end
98
98
99 def test_get_add_by_non_admin
99 def test_get_add_by_non_admin
100 @request.session[:user_id] = 2
100 @request.session[:user_id] = 2
101 get :add
101 get :add
102 assert_response :success
102 assert_response :success
103 assert_template 'add'
103 assert_template 'add'
104 end
104 end
105
105
106 def test_post_add
106 def test_post_add
107 @request.session[:user_id] = 1
107 @request.session[:user_id] = 1
108 post :add, :project => { :name => "blog",
108 post :add, :project => { :name => "blog",
109 :description => "weblog",
109 :description => "weblog",
110 :identifier => "blog",
110 :identifier => "blog",
111 :is_public => 1,
111 :is_public => 1,
112 :custom_field_values => { '3' => 'Beta' }
112 :custom_field_values => { '3' => 'Beta' }
113 }
113 }
114 assert_redirected_to '/projects/blog/settings'
114 assert_redirected_to '/projects/blog/settings'
115
115
116 project = Project.find_by_name('blog')
116 project = Project.find_by_name('blog')
117 assert_kind_of Project, project
117 assert_kind_of Project, project
118 assert_equal 'weblog', project.description
118 assert_equal 'weblog', project.description
119 assert_equal true, project.is_public?
119 assert_equal true, project.is_public?
120 assert_nil project.parent
120 assert_nil project.parent
121 end
121 end
122
122
123 def test_post_add_subproject
123 def test_post_add_subproject
124 @request.session[:user_id] = 1
124 @request.session[:user_id] = 1
125 post :add, :project => { :name => "blog",
125 post :add, :project => { :name => "blog",
126 :description => "weblog",
126 :description => "weblog",
127 :identifier => "blog",
127 :identifier => "blog",
128 :is_public => 1,
128 :is_public => 1,
129 :custom_field_values => { '3' => 'Beta' },
129 :custom_field_values => { '3' => 'Beta' },
130 :parent_id => 1
130 :parent_id => 1
131 }
131 }
132 assert_redirected_to '/projects/blog/settings'
132 assert_redirected_to '/projects/blog/settings'
133
133
134 project = Project.find_by_name('blog')
134 project = Project.find_by_name('blog')
135 assert_kind_of Project, project
135 assert_kind_of Project, project
136 assert_equal Project.find(1), project.parent
136 assert_equal Project.find(1), project.parent
137 end
137 end
138
138
139 def test_post_add_by_non_admin
139 def test_post_add_by_non_admin
140 @request.session[:user_id] = 2
140 @request.session[:user_id] = 2
141 post :add, :project => { :name => "blog",
141 post :add, :project => { :name => "blog",
142 :description => "weblog",
142 :description => "weblog",
143 :identifier => "blog",
143 :identifier => "blog",
144 :is_public => 1,
144 :is_public => 1,
145 :custom_field_values => { '3' => 'Beta' }
145 :custom_field_values => { '3' => 'Beta' }
146 }
146 }
147 assert_redirected_to '/projects/blog/settings'
147 assert_redirected_to '/projects/blog/settings'
148
148
149 project = Project.find_by_name('blog')
149 project = Project.find_by_name('blog')
150 assert_kind_of Project, project
150 assert_kind_of Project, project
151 assert_equal 'weblog', project.description
151 assert_equal 'weblog', project.description
152 assert_equal true, project.is_public?
152 assert_equal true, project.is_public?
153
153
154 # User should be added as a project member
154 # User should be added as a project member
155 assert User.find(2).member_of?(project)
155 assert User.find(2).member_of?(project)
156 assert_equal 1, project.members.size
156 assert_equal 1, project.members.size
157 end
157 end
158
158
159 def test_show_routing
159 def test_show_routing
160 assert_routing(
160 assert_routing(
161 {:method => :get, :path => '/projects/test'},
161 {:method => :get, :path => '/projects/test'},
162 :controller => 'projects', :action => 'show', :id => 'test'
162 :controller => 'projects', :action => 'show', :id => 'test'
163 )
163 )
164 end
164 end
165
165
166 def test_show_by_id
166 def test_show_by_id
167 get :show, :id => 1
167 get :show, :id => 1
168 assert_response :success
168 assert_response :success
169 assert_template 'show'
169 assert_template 'show'
170 assert_not_nil assigns(:project)
170 assert_not_nil assigns(:project)
171 end
171 end
172
172
173 def test_show_by_identifier
173 def test_show_by_identifier
174 get :show, :id => 'ecookbook'
174 get :show, :id => 'ecookbook'
175 assert_response :success
175 assert_response :success
176 assert_template 'show'
176 assert_template 'show'
177 assert_not_nil assigns(:project)
177 assert_not_nil assigns(:project)
178 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
178 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
179 end
179 end
180
180
181 def test_show_should_not_fail_when_custom_values_are_nil
181 def test_show_should_not_fail_when_custom_values_are_nil
182 project = Project.find_by_identifier('ecookbook')
182 project = Project.find_by_identifier('ecookbook')
183 project.custom_values.first.update_attribute(:value, nil)
183 project.custom_values.first.update_attribute(:value, nil)
184 get :show, :id => 'ecookbook'
184 get :show, :id => 'ecookbook'
185 assert_response :success
185 assert_response :success
186 assert_template 'show'
186 assert_template 'show'
187 assert_not_nil assigns(:project)
187 assert_not_nil assigns(:project)
188 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
188 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
189 end
189 end
190
190
191 def test_private_subprojects_hidden
191 def test_private_subprojects_hidden
192 get :show, :id => 'ecookbook'
192 get :show, :id => 'ecookbook'
193 assert_response :success
193 assert_response :success
194 assert_template 'show'
194 assert_template 'show'
195 assert_no_tag :tag => 'a', :content => /Private child/
195 assert_no_tag :tag => 'a', :content => /Private child/
196 end
196 end
197
197
198 def test_private_subprojects_visible
198 def test_private_subprojects_visible
199 @request.session[:user_id] = 2 # manager who is a member of the private subproject
199 @request.session[:user_id] = 2 # manager who is a member of the private subproject
200 get :show, :id => 'ecookbook'
200 get :show, :id => 'ecookbook'
201 assert_response :success
201 assert_response :success
202 assert_template 'show'
202 assert_template 'show'
203 assert_tag :tag => 'a', :content => /Private child/
203 assert_tag :tag => 'a', :content => /Private child/
204 end
204 end
205
205
206 def test_settings_routing
206 def test_settings_routing
207 assert_routing(
207 assert_routing(
208 {:method => :get, :path => '/projects/4223/settings'},
208 {:method => :get, :path => '/projects/4223/settings'},
209 :controller => 'projects', :action => 'settings', :id => '4223'
209 :controller => 'projects', :action => 'settings', :id => '4223'
210 )
210 )
211 assert_routing(
211 assert_routing(
212 {:method => :get, :path => '/projects/4223/settings/members'},
212 {:method => :get, :path => '/projects/4223/settings/members'},
213 :controller => 'projects', :action => 'settings', :id => '4223', :tab => 'members'
213 :controller => 'projects', :action => 'settings', :id => '4223', :tab => 'members'
214 )
214 )
215 end
215 end
216
216
217 def test_settings
217 def test_settings
218 @request.session[:user_id] = 2 # manager
218 @request.session[:user_id] = 2 # manager
219 get :settings, :id => 1
219 get :settings, :id => 1
220 assert_response :success
220 assert_response :success
221 assert_template 'settings'
221 assert_template 'settings'
222 end
222 end
223
223
224 def test_edit
224 def test_edit
225 @request.session[:user_id] = 2 # manager
225 @request.session[:user_id] = 2 # manager
226 post :edit, :id => 1, :project => {:name => 'Test changed name',
226 post :edit, :id => 1, :project => {:name => 'Test changed name',
227 :issue_custom_field_ids => ['']}
227 :issue_custom_field_ids => ['']}
228 assert_redirected_to 'projects/ecookbook/settings'
228 assert_redirected_to 'projects/ecookbook/settings'
229 project = Project.find(1)
229 project = Project.find(1)
230 assert_equal 'Test changed name', project.name
230 assert_equal 'Test changed name', project.name
231 end
231 end
232
232
233 def test_add_version_routing
233 def test_add_version_routing
234 assert_routing(
234 assert_routing(
235 {:method => :get, :path => 'projects/64/versions/new'},
235 {:method => :get, :path => 'projects/64/versions/new'},
236 :controller => 'projects', :action => 'add_version', :id => '64'
236 :controller => 'projects', :action => 'add_version', :id => '64'
237 )
237 )
238 assert_routing(
238 assert_routing(
239 #TODO: use PUT
239 #TODO: use PUT
240 {:method => :post, :path => 'projects/64/versions/new'},
240 {:method => :post, :path => 'projects/64/versions/new'},
241 :controller => 'projects', :action => 'add_version', :id => '64'
241 :controller => 'projects', :action => 'add_version', :id => '64'
242 )
242 )
243 end
243 end
244
244
245 def test_add_version
246 @request.session[:user_id] = 2 # manager
247 assert_difference 'Version.count' do
248 post :add_version, :id => '1', :version => {:name => 'test_add_version'}
249 end
250 assert_redirected_to '/projects/ecookbook/settings/versions'
251 version = Version.find_by_name('test_add_version')
252 assert_not_nil version
253 assert_equal 1, version.project_id
254 end
255
256 def test_add_version_from_issue_form
257 @request.session[:user_id] = 2 # manager
258 assert_difference 'Version.count' do
259 xhr :post, :add_version, :id => '1', :version => {:name => 'test_add_version_from_issue_form'}
260 end
261 assert_response :success
262 assert_select_rjs :replace, 'issue_fixed_version_id'
263 version = Version.find_by_name('test_add_version_from_issue_form')
264 assert_not_nil version
265 assert_equal 1, version.project_id
266 end
267
245 def test_add_issue_category_routing
268 def test_add_issue_category_routing
246 assert_routing(
269 assert_routing(
247 {:method => :get, :path => 'projects/test/categories/new'},
270 {:method => :get, :path => 'projects/test/categories/new'},
248 :controller => 'projects', :action => 'add_issue_category', :id => 'test'
271 :controller => 'projects', :action => 'add_issue_category', :id => 'test'
249 )
272 )
250 assert_routing(
273 assert_routing(
251 #TODO: use PUT and update form
274 #TODO: use PUT and update form
252 {:method => :post, :path => 'projects/64/categories/new'},
275 {:method => :post, :path => 'projects/64/categories/new'},
253 :controller => 'projects', :action => 'add_issue_category', :id => '64'
276 :controller => 'projects', :action => 'add_issue_category', :id => '64'
254 )
277 )
255 end
278 end
256
279
257 def test_destroy_routing
280 def test_destroy_routing
258 assert_routing(
281 assert_routing(
259 {:method => :get, :path => '/projects/567/destroy'},
282 {:method => :get, :path => '/projects/567/destroy'},
260 :controller => 'projects', :action => 'destroy', :id => '567'
283 :controller => 'projects', :action => 'destroy', :id => '567'
261 )
284 )
262 assert_routing(
285 assert_routing(
263 #TODO: use DELETE and update form
286 #TODO: use DELETE and update form
264 {:method => :post, :path => 'projects/64/destroy'},
287 {:method => :post, :path => 'projects/64/destroy'},
265 :controller => 'projects', :action => 'destroy', :id => '64'
288 :controller => 'projects', :action => 'destroy', :id => '64'
266 )
289 )
267 end
290 end
268
291
269 def test_get_destroy
292 def test_get_destroy
270 @request.session[:user_id] = 1 # admin
293 @request.session[:user_id] = 1 # admin
271 get :destroy, :id => 1
294 get :destroy, :id => 1
272 assert_response :success
295 assert_response :success
273 assert_template 'destroy'
296 assert_template 'destroy'
274 assert_not_nil Project.find_by_id(1)
297 assert_not_nil Project.find_by_id(1)
275 end
298 end
276
299
277 def test_post_destroy
300 def test_post_destroy
278 @request.session[:user_id] = 1 # admin
301 @request.session[:user_id] = 1 # admin
279 post :destroy, :id => 1, :confirm => 1
302 post :destroy, :id => 1, :confirm => 1
280 assert_redirected_to 'admin/projects'
303 assert_redirected_to 'admin/projects'
281 assert_nil Project.find_by_id(1)
304 assert_nil Project.find_by_id(1)
282 end
305 end
283
306
284 def test_add_file
307 def test_add_file
285 set_tmp_attachments_directory
308 set_tmp_attachments_directory
286 @request.session[:user_id] = 2
309 @request.session[:user_id] = 2
287 Setting.notified_events = ['file_added']
310 Setting.notified_events = ['file_added']
288 ActionMailer::Base.deliveries.clear
311 ActionMailer::Base.deliveries.clear
289
312
290 assert_difference 'Attachment.count' do
313 assert_difference 'Attachment.count' do
291 post :add_file, :id => 1, :version_id => '',
314 post :add_file, :id => 1, :version_id => '',
292 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
315 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
293 end
316 end
294 assert_redirected_to 'projects/ecookbook/files'
317 assert_redirected_to 'projects/ecookbook/files'
295 a = Attachment.find(:first, :order => 'created_on DESC')
318 a = Attachment.find(:first, :order => 'created_on DESC')
296 assert_equal 'testfile.txt', a.filename
319 assert_equal 'testfile.txt', a.filename
297 assert_equal Project.find(1), a.container
320 assert_equal Project.find(1), a.container
298
321
299 mail = ActionMailer::Base.deliveries.last
322 mail = ActionMailer::Base.deliveries.last
300 assert_kind_of TMail::Mail, mail
323 assert_kind_of TMail::Mail, mail
301 assert_equal "[eCookbook] New file", mail.subject
324 assert_equal "[eCookbook] New file", mail.subject
302 assert mail.body.include?('testfile.txt')
325 assert mail.body.include?('testfile.txt')
303 end
326 end
304
327
305 def test_add_file_routing
328 def test_add_file_routing
306 assert_routing(
329 assert_routing(
307 {:method => :get, :path => '/projects/33/files/new'},
330 {:method => :get, :path => '/projects/33/files/new'},
308 :controller => 'projects', :action => 'add_file', :id => '33'
331 :controller => 'projects', :action => 'add_file', :id => '33'
309 )
332 )
310 assert_routing(
333 assert_routing(
311 {:method => :post, :path => '/projects/33/files/new'},
334 {:method => :post, :path => '/projects/33/files/new'},
312 :controller => 'projects', :action => 'add_file', :id => '33'
335 :controller => 'projects', :action => 'add_file', :id => '33'
313 )
336 )
314 end
337 end
315
338
316 def test_add_version_file
339 def test_add_version_file
317 set_tmp_attachments_directory
340 set_tmp_attachments_directory
318 @request.session[:user_id] = 2
341 @request.session[:user_id] = 2
319 Setting.notified_events = ['file_added']
342 Setting.notified_events = ['file_added']
320
343
321 assert_difference 'Attachment.count' do
344 assert_difference 'Attachment.count' do
322 post :add_file, :id => 1, :version_id => '2',
345 post :add_file, :id => 1, :version_id => '2',
323 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
346 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
324 end
347 end
325 assert_redirected_to 'projects/ecookbook/files'
348 assert_redirected_to 'projects/ecookbook/files'
326 a = Attachment.find(:first, :order => 'created_on DESC')
349 a = Attachment.find(:first, :order => 'created_on DESC')
327 assert_equal 'testfile.txt', a.filename
350 assert_equal 'testfile.txt', a.filename
328 assert_equal Version.find(2), a.container
351 assert_equal Version.find(2), a.container
329 end
352 end
330
353
331 def test_list_files
354 def test_list_files
332 get :list_files, :id => 1
355 get :list_files, :id => 1
333 assert_response :success
356 assert_response :success
334 assert_template 'list_files'
357 assert_template 'list_files'
335 assert_not_nil assigns(:containers)
358 assert_not_nil assigns(:containers)
336
359
337 # file attached to the project
360 # file attached to the project
338 assert_tag :a, :content => 'project_file.zip',
361 assert_tag :a, :content => 'project_file.zip',
339 :attributes => { :href => '/attachments/download/8/project_file.zip' }
362 :attributes => { :href => '/attachments/download/8/project_file.zip' }
340
363
341 # file attached to a project's version
364 # file attached to a project's version
342 assert_tag :a, :content => 'version_file.zip',
365 assert_tag :a, :content => 'version_file.zip',
343 :attributes => { :href => '/attachments/download/9/version_file.zip' }
366 :attributes => { :href => '/attachments/download/9/version_file.zip' }
344 end
367 end
345
368
346 def test_list_files_routing
369 def test_list_files_routing
347 assert_routing(
370 assert_routing(
348 {:method => :get, :path => '/projects/33/files'},
371 {:method => :get, :path => '/projects/33/files'},
349 :controller => 'projects', :action => 'list_files', :id => '33'
372 :controller => 'projects', :action => 'list_files', :id => '33'
350 )
373 )
351 end
374 end
352
375
353 def test_changelog_routing
376 def test_changelog_routing
354 assert_routing(
377 assert_routing(
355 {:method => :get, :path => '/projects/44/changelog'},
378 {:method => :get, :path => '/projects/44/changelog'},
356 :controller => 'projects', :action => 'changelog', :id => '44'
379 :controller => 'projects', :action => 'changelog', :id => '44'
357 )
380 )
358 end
381 end
359
382
360 def test_changelog
383 def test_changelog
361 get :changelog, :id => 1
384 get :changelog, :id => 1
362 assert_response :success
385 assert_response :success
363 assert_template 'changelog'
386 assert_template 'changelog'
364 assert_not_nil assigns(:versions)
387 assert_not_nil assigns(:versions)
365 end
388 end
366
389
367 def test_changelog_showing_subprojects_versions
390 def test_changelog_showing_subprojects_versions
368 get :changelog, :id => 1, :with_subprojects => 1
391 get :changelog, :id => 1, :with_subprojects => 1
369 assert_response :success
392 assert_response :success
370 assert_template 'changelog'
393 assert_template 'changelog'
371 assert_not_nil assigns(:versions)
394 assert_not_nil assigns(:versions)
372 # Version on subproject appears
395 # Version on subproject appears
373 assert assigns(:versions).include?(Version.find(4))
396 assert assigns(:versions).include?(Version.find(4))
374 end
397 end
375
398
376 def test_roadmap_routing
399 def test_roadmap_routing
377 assert_routing(
400 assert_routing(
378 {:method => :get, :path => 'projects/33/roadmap'},
401 {:method => :get, :path => 'projects/33/roadmap'},
379 :controller => 'projects', :action => 'roadmap', :id => '33'
402 :controller => 'projects', :action => 'roadmap', :id => '33'
380 )
403 )
381 end
404 end
382
405
383 def test_roadmap
406 def test_roadmap
384 get :roadmap, :id => 1
407 get :roadmap, :id => 1
385 assert_response :success
408 assert_response :success
386 assert_template 'roadmap'
409 assert_template 'roadmap'
387 assert_not_nil assigns(:versions)
410 assert_not_nil assigns(:versions)
388 # Version with no date set appears
411 # Version with no date set appears
389 assert assigns(:versions).include?(Version.find(3))
412 assert assigns(:versions).include?(Version.find(3))
390 # Completed version doesn't appear
413 # Completed version doesn't appear
391 assert !assigns(:versions).include?(Version.find(1))
414 assert !assigns(:versions).include?(Version.find(1))
392 end
415 end
393
416
394 def test_roadmap_with_completed_versions
417 def test_roadmap_with_completed_versions
395 get :roadmap, :id => 1, :completed => 1
418 get :roadmap, :id => 1, :completed => 1
396 assert_response :success
419 assert_response :success
397 assert_template 'roadmap'
420 assert_template 'roadmap'
398 assert_not_nil assigns(:versions)
421 assert_not_nil assigns(:versions)
399 # Version with no date set appears
422 # Version with no date set appears
400 assert assigns(:versions).include?(Version.find(3))
423 assert assigns(:versions).include?(Version.find(3))
401 # Completed version appears
424 # Completed version appears
402 assert assigns(:versions).include?(Version.find(1))
425 assert assigns(:versions).include?(Version.find(1))
403 end
426 end
404
427
405 def test_roadmap_showing_subprojects_versions
428 def test_roadmap_showing_subprojects_versions
406 get :roadmap, :id => 1, :with_subprojects => 1
429 get :roadmap, :id => 1, :with_subprojects => 1
407 assert_response :success
430 assert_response :success
408 assert_template 'roadmap'
431 assert_template 'roadmap'
409 assert_not_nil assigns(:versions)
432 assert_not_nil assigns(:versions)
410 # Version on subproject appears
433 # Version on subproject appears
411 assert assigns(:versions).include?(Version.find(4))
434 assert assigns(:versions).include?(Version.find(4))
412 end
435 end
413
436
414 def test_project_activity_routing
437 def test_project_activity_routing
415 assert_routing(
438 assert_routing(
416 {:method => :get, :path => '/projects/1/activity'},
439 {:method => :get, :path => '/projects/1/activity'},
417 :controller => 'projects', :action => 'activity', :id => '1'
440 :controller => 'projects', :action => 'activity', :id => '1'
418 )
441 )
419 end
442 end
420
443
421 def test_project_activity_atom_routing
444 def test_project_activity_atom_routing
422 assert_routing(
445 assert_routing(
423 {:method => :get, :path => '/projects/1/activity.atom'},
446 {:method => :get, :path => '/projects/1/activity.atom'},
424 :controller => 'projects', :action => 'activity', :id => '1', :format => 'atom'
447 :controller => 'projects', :action => 'activity', :id => '1', :format => 'atom'
425 )
448 )
426 end
449 end
427
450
428 def test_project_activity
451 def test_project_activity
429 get :activity, :id => 1, :with_subprojects => 0
452 get :activity, :id => 1, :with_subprojects => 0
430 assert_response :success
453 assert_response :success
431 assert_template 'activity'
454 assert_template 'activity'
432 assert_not_nil assigns(:events_by_day)
455 assert_not_nil assigns(:events_by_day)
433
456
434 assert_tag :tag => "h3",
457 assert_tag :tag => "h3",
435 :content => /#{2.days.ago.to_date.day}/,
458 :content => /#{2.days.ago.to_date.day}/,
436 :sibling => { :tag => "dl",
459 :sibling => { :tag => "dl",
437 :child => { :tag => "dt",
460 :child => { :tag => "dt",
438 :attributes => { :class => /issue-edit/ },
461 :attributes => { :class => /issue-edit/ },
439 :child => { :tag => "a",
462 :child => { :tag => "a",
440 :content => /(#{IssueStatus.find(2).name})/,
463 :content => /(#{IssueStatus.find(2).name})/,
441 }
464 }
442 }
465 }
443 }
466 }
444 end
467 end
445
468
446 def test_previous_project_activity
469 def test_previous_project_activity
447 get :activity, :id => 1, :from => 3.days.ago.to_date
470 get :activity, :id => 1, :from => 3.days.ago.to_date
448 assert_response :success
471 assert_response :success
449 assert_template 'activity'
472 assert_template 'activity'
450 assert_not_nil assigns(:events_by_day)
473 assert_not_nil assigns(:events_by_day)
451
474
452 assert_tag :tag => "h3",
475 assert_tag :tag => "h3",
453 :content => /#{3.day.ago.to_date.day}/,
476 :content => /#{3.day.ago.to_date.day}/,
454 :sibling => { :tag => "dl",
477 :sibling => { :tag => "dl",
455 :child => { :tag => "dt",
478 :child => { :tag => "dt",
456 :attributes => { :class => /issue/ },
479 :attributes => { :class => /issue/ },
457 :child => { :tag => "a",
480 :child => { :tag => "a",
458 :content => /#{Issue.find(1).subject}/,
481 :content => /#{Issue.find(1).subject}/,
459 }
482 }
460 }
483 }
461 }
484 }
462 end
485 end
463
486
464 def test_global_activity_routing
487 def test_global_activity_routing
465 assert_routing({:method => :get, :path => '/activity'}, :controller => 'projects', :action => 'activity', :id => nil)
488 assert_routing({:method => :get, :path => '/activity'}, :controller => 'projects', :action => 'activity', :id => nil)
466 end
489 end
467
490
468 def test_global_activity
491 def test_global_activity
469 get :activity
492 get :activity
470 assert_response :success
493 assert_response :success
471 assert_template 'activity'
494 assert_template 'activity'
472 assert_not_nil assigns(:events_by_day)
495 assert_not_nil assigns(:events_by_day)
473
496
474 assert_tag :tag => "h3",
497 assert_tag :tag => "h3",
475 :content => /#{5.day.ago.to_date.day}/,
498 :content => /#{5.day.ago.to_date.day}/,
476 :sibling => { :tag => "dl",
499 :sibling => { :tag => "dl",
477 :child => { :tag => "dt",
500 :child => { :tag => "dt",
478 :attributes => { :class => /issue/ },
501 :attributes => { :class => /issue/ },
479 :child => { :tag => "a",
502 :child => { :tag => "a",
480 :content => /#{Issue.find(5).subject}/,
503 :content => /#{Issue.find(5).subject}/,
481 }
504 }
482 }
505 }
483 }
506 }
484 end
507 end
485
508
486 def test_user_activity
509 def test_user_activity
487 get :activity, :user_id => 2
510 get :activity, :user_id => 2
488 assert_response :success
511 assert_response :success
489 assert_template 'activity'
512 assert_template 'activity'
490 assert_not_nil assigns(:events_by_day)
513 assert_not_nil assigns(:events_by_day)
491
514
492 assert_tag :tag => "h3",
515 assert_tag :tag => "h3",
493 :content => /#{3.day.ago.to_date.day}/,
516 :content => /#{3.day.ago.to_date.day}/,
494 :sibling => { :tag => "dl",
517 :sibling => { :tag => "dl",
495 :child => { :tag => "dt",
518 :child => { :tag => "dt",
496 :attributes => { :class => /issue/ },
519 :attributes => { :class => /issue/ },
497 :child => { :tag => "a",
520 :child => { :tag => "a",
498 :content => /#{Issue.find(1).subject}/,
521 :content => /#{Issue.find(1).subject}/,
499 }
522 }
500 }
523 }
501 }
524 }
502 end
525 end
503
526
504 def test_global_activity_atom_routing
527 def test_global_activity_atom_routing
505 assert_routing({:method => :get, :path => '/activity.atom'}, :controller => 'projects', :action => 'activity', :id => nil, :format => 'atom')
528 assert_routing({:method => :get, :path => '/activity.atom'}, :controller => 'projects', :action => 'activity', :id => nil, :format => 'atom')
506 end
529 end
507
530
508 def test_activity_atom_feed
531 def test_activity_atom_feed
509 get :activity, :format => 'atom'
532 get :activity, :format => 'atom'
510 assert_response :success
533 assert_response :success
511 assert_template 'common/feed.atom.rxml'
534 assert_template 'common/feed.atom.rxml'
512 end
535 end
513
536
514 def test_archive_routing
537 def test_archive_routing
515 assert_routing(
538 assert_routing(
516 #TODO: use PUT to project path and modify form
539 #TODO: use PUT to project path and modify form
517 {:method => :post, :path => 'projects/64/archive'},
540 {:method => :post, :path => 'projects/64/archive'},
518 :controller => 'projects', :action => 'archive', :id => '64'
541 :controller => 'projects', :action => 'archive', :id => '64'
519 )
542 )
520 end
543 end
521
544
522 def test_archive
545 def test_archive
523 @request.session[:user_id] = 1 # admin
546 @request.session[:user_id] = 1 # admin
524 post :archive, :id => 1
547 post :archive, :id => 1
525 assert_redirected_to 'admin/projects'
548 assert_redirected_to 'admin/projects'
526 assert !Project.find(1).active?
549 assert !Project.find(1).active?
527 end
550 end
528
551
529 def test_unarchive_routing
552 def test_unarchive_routing
530 assert_routing(
553 assert_routing(
531 #TODO: use PUT to project path and modify form
554 #TODO: use PUT to project path and modify form
532 {:method => :post, :path => '/projects/567/unarchive'},
555 {:method => :post, :path => '/projects/567/unarchive'},
533 :controller => 'projects', :action => 'unarchive', :id => '567'
556 :controller => 'projects', :action => 'unarchive', :id => '567'
534 )
557 )
535 end
558 end
536
559
537 def test_unarchive
560 def test_unarchive
538 @request.session[:user_id] = 1 # admin
561 @request.session[:user_id] = 1 # admin
539 Project.find(1).archive
562 Project.find(1).archive
540 post :unarchive, :id => 1
563 post :unarchive, :id => 1
541 assert_redirected_to 'admin/projects'
564 assert_redirected_to 'admin/projects'
542 assert Project.find(1).active?
565 assert Project.find(1).active?
543 end
566 end
544
567
545 def test_project_breadcrumbs_should_be_limited_to_3_ancestors
568 def test_project_breadcrumbs_should_be_limited_to_3_ancestors
546 CustomField.delete_all
569 CustomField.delete_all
547 parent = nil
570 parent = nil
548 6.times do |i|
571 6.times do |i|
549 p = Project.create!(:name => "Breadcrumbs #{i}", :identifier => "breadcrumbs-#{i}")
572 p = Project.create!(:name => "Breadcrumbs #{i}", :identifier => "breadcrumbs-#{i}")
550 p.set_parent!(parent)
573 p.set_parent!(parent)
551 get :show, :id => p
574 get :show, :id => p
552 assert_tag :h1, :parent => { :attributes => {:id => 'header'}},
575 assert_tag :h1, :parent => { :attributes => {:id => 'header'}},
553 :children => { :count => [i, 3].min,
576 :children => { :count => [i, 3].min,
554 :only => { :tag => 'a' } }
577 :only => { :tag => 'a' } }
555
578
556 parent = p
579 parent = p
557 end
580 end
558 end
581 end
559
582
560 def test_copy_with_project
583 def test_copy_with_project
561 @request.session[:user_id] = 1 # admin
584 @request.session[:user_id] = 1 # admin
562 get :copy, :id => 1
585 get :copy, :id => 1
563 assert_response :success
586 assert_response :success
564 assert_template 'copy'
587 assert_template 'copy'
565 assert assigns(:project)
588 assert assigns(:project)
566 assert_equal Project.find(1).description, assigns(:project).description
589 assert_equal Project.find(1).description, assigns(:project).description
567 assert_nil assigns(:project).id
590 assert_nil assigns(:project).id
568 end
591 end
569
592
570 def test_copy_without_project
593 def test_copy_without_project
571 @request.session[:user_id] = 1 # admin
594 @request.session[:user_id] = 1 # admin
572 get :copy
595 get :copy
573 assert_response :redirect
596 assert_response :redirect
574 assert_redirected_to :controller => 'admin', :action => 'projects'
597 assert_redirected_to :controller => 'admin', :action => 'projects'
575 end
598 end
576
599
577 def test_jump_should_redirect_to_active_tab
600 def test_jump_should_redirect_to_active_tab
578 get :show, :id => 1, :jump => 'issues'
601 get :show, :id => 1, :jump => 'issues'
579 assert_redirected_to 'projects/ecookbook/issues'
602 assert_redirected_to 'projects/ecookbook/issues'
580 end
603 end
581
604
582 def test_jump_should_not_redirect_to_inactive_tab
605 def test_jump_should_not_redirect_to_inactive_tab
583 get :show, :id => 3, :jump => 'documents'
606 get :show, :id => 3, :jump => 'documents'
584 assert_response :success
607 assert_response :success
585 assert_template 'show'
608 assert_template 'show'
586 end
609 end
587
610
588 def test_jump_should_not_redirect_to_unknown_tab
611 def test_jump_should_not_redirect_to_unknown_tab
589 get :show, :id => 3, :jump => 'foobar'
612 get :show, :id => 3, :jump => 'foobar'
590 assert_response :success
613 assert_response :success
591 assert_template 'show'
614 assert_template 'show'
592 end
615 end
593
616
594 def test_reset_activities_routing
617 def test_reset_activities_routing
595 assert_routing({:method => :delete, :path => 'projects/64/reset_activities'},
618 assert_routing({:method => :delete, :path => 'projects/64/reset_activities'},
596 :controller => 'projects', :action => 'reset_activities', :id => '64')
619 :controller => 'projects', :action => 'reset_activities', :id => '64')
597 end
620 end
598
621
599 def test_reset_activities
622 def test_reset_activities
600 @request.session[:user_id] = 2 # manager
623 @request.session[:user_id] = 2 # manager
601 project_activity = TimeEntryActivity.new({
624 project_activity = TimeEntryActivity.new({
602 :name => 'Project Specific',
625 :name => 'Project Specific',
603 :parent => TimeEntryActivity.find(:first),
626 :parent => TimeEntryActivity.find(:first),
604 :project => Project.find(1),
627 :project => Project.find(1),
605 :active => true
628 :active => true
606 })
629 })
607 assert project_activity.save
630 assert project_activity.save
608 project_activity_two = TimeEntryActivity.new({
631 project_activity_two = TimeEntryActivity.new({
609 :name => 'Project Specific Two',
632 :name => 'Project Specific Two',
610 :parent => TimeEntryActivity.find(:last),
633 :parent => TimeEntryActivity.find(:last),
611 :project => Project.find(1),
634 :project => Project.find(1),
612 :active => true
635 :active => true
613 })
636 })
614 assert project_activity_two.save
637 assert project_activity_two.save
615
638
616 delete :reset_activities, :id => 1
639 delete :reset_activities, :id => 1
617 assert_response :redirect
640 assert_response :redirect
618 assert_redirected_to 'projects/ecookbook/settings/activities'
641 assert_redirected_to 'projects/ecookbook/settings/activities'
619
642
620 assert_nil TimeEntryActivity.find_by_id(project_activity.id)
643 assert_nil TimeEntryActivity.find_by_id(project_activity.id)
621 assert_nil TimeEntryActivity.find_by_id(project_activity_two.id)
644 assert_nil TimeEntryActivity.find_by_id(project_activity_two.id)
622 end
645 end
623
646
624 def test_reset_activities_should_reassign_time_entries_back_to_the_system_activity
647 def test_reset_activities_should_reassign_time_entries_back_to_the_system_activity
625 @request.session[:user_id] = 2 # manager
648 @request.session[:user_id] = 2 # manager
626 project_activity = TimeEntryActivity.new({
649 project_activity = TimeEntryActivity.new({
627 :name => 'Project Specific Design',
650 :name => 'Project Specific Design',
628 :parent => TimeEntryActivity.find(9),
651 :parent => TimeEntryActivity.find(9),
629 :project => Project.find(1),
652 :project => Project.find(1),
630 :active => true
653 :active => true
631 })
654 })
632 assert project_activity.save
655 assert project_activity.save
633 assert TimeEntry.update_all("activity_id = '#{project_activity.id}'", ["project_id = ? AND activity_id = ?", 1, 9])
656 assert TimeEntry.update_all("activity_id = '#{project_activity.id}'", ["project_id = ? AND activity_id = ?", 1, 9])
634 assert 3, TimeEntry.find_all_by_activity_id_and_project_id(project_activity.id, 1).size
657 assert 3, TimeEntry.find_all_by_activity_id_and_project_id(project_activity.id, 1).size
635
658
636 delete :reset_activities, :id => 1
659 delete :reset_activities, :id => 1
637 assert_response :redirect
660 assert_response :redirect
638 assert_redirected_to 'projects/ecookbook/settings/activities'
661 assert_redirected_to 'projects/ecookbook/settings/activities'
639
662
640 assert_nil TimeEntryActivity.find_by_id(project_activity.id)
663 assert_nil TimeEntryActivity.find_by_id(project_activity.id)
641 assert_equal 0, TimeEntry.find_all_by_activity_id_and_project_id(project_activity.id, 1).size, "TimeEntries still assigned to project specific activity"
664 assert_equal 0, TimeEntry.find_all_by_activity_id_and_project_id(project_activity.id, 1).size, "TimeEntries still assigned to project specific activity"
642 assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "TimeEntries still assigned to project specific activity"
665 assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "TimeEntries still assigned to project specific activity"
643 end
666 end
644
667
645 def test_save_activities_routing
668 def test_save_activities_routing
646 assert_routing({:method => :post, :path => 'projects/64/activities/save'},
669 assert_routing({:method => :post, :path => 'projects/64/activities/save'},
647 :controller => 'projects', :action => 'save_activities', :id => '64')
670 :controller => 'projects', :action => 'save_activities', :id => '64')
648 end
671 end
649
672
650 def test_save_activities_to_override_system_activities
673 def test_save_activities_to_override_system_activities
651 @request.session[:user_id] = 2 # manager
674 @request.session[:user_id] = 2 # manager
652 billable_field = TimeEntryActivityCustomField.find_by_name("Billable")
675 billable_field = TimeEntryActivityCustomField.find_by_name("Billable")
653
676
654 post :save_activities, :id => 1, :enumerations => {
677 post :save_activities, :id => 1, :enumerations => {
655 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design, De-activate
678 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design, De-activate
656 "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"}, # Development, Change custom value
679 "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"}, # Development, Change custom value
657 "14"=>{"parent_id"=>"14", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"}, # Inactive Activity, Activate with custom value
680 "14"=>{"parent_id"=>"14", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"}, # Inactive Activity, Activate with custom value
658 "11"=>{"parent_id"=>"11", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"} # QA, no changes
681 "11"=>{"parent_id"=>"11", "custom_field_values"=>{"7"=>"1"}, "active"=>"1"} # QA, no changes
659 }
682 }
660
683
661 assert_response :redirect
684 assert_response :redirect
662 assert_redirected_to 'projects/ecookbook/settings/activities'
685 assert_redirected_to 'projects/ecookbook/settings/activities'
663
686
664 # Created project specific activities...
687 # Created project specific activities...
665 project = Project.find('ecookbook')
688 project = Project.find('ecookbook')
666
689
667 # ... Design
690 # ... Design
668 design = project.time_entry_activities.find_by_name("Design")
691 design = project.time_entry_activities.find_by_name("Design")
669 assert design, "Project activity not found"
692 assert design, "Project activity not found"
670
693
671 assert_equal 9, design.parent_id # Relate to the system activity
694 assert_equal 9, design.parent_id # Relate to the system activity
672 assert_not_equal design.parent.id, design.id # Different records
695 assert_not_equal design.parent.id, design.id # Different records
673 assert_equal design.parent.name, design.name # Same name
696 assert_equal design.parent.name, design.name # Same name
674 assert !design.active?
697 assert !design.active?
675
698
676 # ... Development
699 # ... Development
677 development = project.time_entry_activities.find_by_name("Development")
700 development = project.time_entry_activities.find_by_name("Development")
678 assert development, "Project activity not found"
701 assert development, "Project activity not found"
679
702
680 assert_equal 10, development.parent_id # Relate to the system activity
703 assert_equal 10, development.parent_id # Relate to the system activity
681 assert_not_equal development.parent.id, development.id # Different records
704 assert_not_equal development.parent.id, development.id # Different records
682 assert_equal development.parent.name, development.name # Same name
705 assert_equal development.parent.name, development.name # Same name
683 assert development.active?
706 assert development.active?
684 assert_equal "0", development.custom_value_for(billable_field).value
707 assert_equal "0", development.custom_value_for(billable_field).value
685
708
686 # ... Inactive Activity
709 # ... Inactive Activity
687 previously_inactive = project.time_entry_activities.find_by_name("Inactive Activity")
710 previously_inactive = project.time_entry_activities.find_by_name("Inactive Activity")
688 assert previously_inactive, "Project activity not found"
711 assert previously_inactive, "Project activity not found"
689
712
690 assert_equal 14, previously_inactive.parent_id # Relate to the system activity
713 assert_equal 14, previously_inactive.parent_id # Relate to the system activity
691 assert_not_equal previously_inactive.parent.id, previously_inactive.id # Different records
714 assert_not_equal previously_inactive.parent.id, previously_inactive.id # Different records
692 assert_equal previously_inactive.parent.name, previously_inactive.name # Same name
715 assert_equal previously_inactive.parent.name, previously_inactive.name # Same name
693 assert previously_inactive.active?
716 assert previously_inactive.active?
694 assert_equal "1", previously_inactive.custom_value_for(billable_field).value
717 assert_equal "1", previously_inactive.custom_value_for(billable_field).value
695
718
696 # ... QA
719 # ... QA
697 assert_equal nil, project.time_entry_activities.find_by_name("QA"), "Custom QA activity created when it wasn't modified"
720 assert_equal nil, project.time_entry_activities.find_by_name("QA"), "Custom QA activity created when it wasn't modified"
698 end
721 end
699
722
700 def test_save_activities_will_update_project_specific_activities
723 def test_save_activities_will_update_project_specific_activities
701 @request.session[:user_id] = 2 # manager
724 @request.session[:user_id] = 2 # manager
702
725
703 project_activity = TimeEntryActivity.new({
726 project_activity = TimeEntryActivity.new({
704 :name => 'Project Specific',
727 :name => 'Project Specific',
705 :parent => TimeEntryActivity.find(:first),
728 :parent => TimeEntryActivity.find(:first),
706 :project => Project.find(1),
729 :project => Project.find(1),
707 :active => true
730 :active => true
708 })
731 })
709 assert project_activity.save
732 assert project_activity.save
710 project_activity_two = TimeEntryActivity.new({
733 project_activity_two = TimeEntryActivity.new({
711 :name => 'Project Specific Two',
734 :name => 'Project Specific Two',
712 :parent => TimeEntryActivity.find(:last),
735 :parent => TimeEntryActivity.find(:last),
713 :project => Project.find(1),
736 :project => Project.find(1),
714 :active => true
737 :active => true
715 })
738 })
716 assert project_activity_two.save
739 assert project_activity_two.save
717
740
718
741
719 post :save_activities, :id => 1, :enumerations => {
742 post :save_activities, :id => 1, :enumerations => {
720 project_activity.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # De-activate
743 project_activity.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # De-activate
721 project_activity_two.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"} # De-activate
744 project_activity_two.id => {"custom_field_values"=>{"7" => "1"}, "active"=>"0"} # De-activate
722 }
745 }
723
746
724 assert_response :redirect
747 assert_response :redirect
725 assert_redirected_to 'projects/ecookbook/settings/activities'
748 assert_redirected_to 'projects/ecookbook/settings/activities'
726
749
727 # Created project specific activities...
750 # Created project specific activities...
728 project = Project.find('ecookbook')
751 project = Project.find('ecookbook')
729 assert_equal 2, project.time_entry_activities.count
752 assert_equal 2, project.time_entry_activities.count
730
753
731 activity_one = project.time_entry_activities.find_by_name(project_activity.name)
754 activity_one = project.time_entry_activities.find_by_name(project_activity.name)
732 assert activity_one, "Project activity not found"
755 assert activity_one, "Project activity not found"
733 assert_equal project_activity.id, activity_one.id
756 assert_equal project_activity.id, activity_one.id
734 assert !activity_one.active?
757 assert !activity_one.active?
735
758
736 activity_two = project.time_entry_activities.find_by_name(project_activity_two.name)
759 activity_two = project.time_entry_activities.find_by_name(project_activity_two.name)
737 assert activity_two, "Project activity not found"
760 assert activity_two, "Project activity not found"
738 assert_equal project_activity_two.id, activity_two.id
761 assert_equal project_activity_two.id, activity_two.id
739 assert !activity_two.active?
762 assert !activity_two.active?
740 end
763 end
741
764
742 def test_save_activities_when_creating_new_activities_will_convert_existing_data
765 def test_save_activities_when_creating_new_activities_will_convert_existing_data
743 assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size
766 assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size
744
767
745 @request.session[:user_id] = 2 # manager
768 @request.session[:user_id] = 2 # manager
746 post :save_activities, :id => 1, :enumerations => {
769 post :save_activities, :id => 1, :enumerations => {
747 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"} # Design, De-activate
770 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"} # Design, De-activate
748 }
771 }
749 assert_response :redirect
772 assert_response :redirect
750
773
751 # No more TimeEntries using the system activity
774 # No more TimeEntries using the system activity
752 assert_equal 0, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "Time Entries still assigned to system activities"
775 assert_equal 0, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "Time Entries still assigned to system activities"
753 # All TimeEntries using project activity
776 # All TimeEntries using project activity
754 project_specific_activity = TimeEntryActivity.find_by_parent_id_and_project_id(9, 1)
777 project_specific_activity = TimeEntryActivity.find_by_parent_id_and_project_id(9, 1)
755 assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(project_specific_activity.id, 1).size, "No Time Entries assigned to the project activity"
778 assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(project_specific_activity.id, 1).size, "No Time Entries assigned to the project activity"
756 end
779 end
757
780
758 def test_save_activities_when_creating_new_activities_will_not_convert_existing_data_if_an_exception_is_raised
781 def test_save_activities_when_creating_new_activities_will_not_convert_existing_data_if_an_exception_is_raised
759 # TODO: Need to cause an exception on create but these tests
782 # TODO: Need to cause an exception on create but these tests
760 # aren't setup for mocking. Just create a record now so the
783 # aren't setup for mocking. Just create a record now so the
761 # second one is a dupicate
784 # second one is a dupicate
762 parent = TimeEntryActivity.find(9)
785 parent = TimeEntryActivity.find(9)
763 TimeEntryActivity.create!({:name => parent.name, :project_id => 1, :position => parent.position, :active => true})
786 TimeEntryActivity.create!({:name => parent.name, :project_id => 1, :position => parent.position, :active => true})
764 TimeEntry.create!({:project_id => 1, :hours => 1.0, :user => User.find(1), :issue_id => 3, :activity_id => 10, :spent_on => '2009-01-01'})
787 TimeEntry.create!({:project_id => 1, :hours => 1.0, :user => User.find(1), :issue_id => 3, :activity_id => 10, :spent_on => '2009-01-01'})
765
788
766 assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size
789 assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size
767 assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size
790 assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size
768
791
769 @request.session[:user_id] = 2 # manager
792 @request.session[:user_id] = 2 # manager
770 post :save_activities, :id => 1, :enumerations => {
793 post :save_activities, :id => 1, :enumerations => {
771 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design
794 "9"=> {"parent_id"=>"9", "custom_field_values"=>{"7" => "1"}, "active"=>"0"}, # Design
772 "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"} # Development, Change custom value
795 "10"=> {"parent_id"=>"10", "custom_field_values"=>{"7"=>"0"}, "active"=>"1"} # Development, Change custom value
773 }
796 }
774 assert_response :redirect
797 assert_response :redirect
775
798
776 # TimeEntries shouldn't have been reassigned on the failed record
799 # TimeEntries shouldn't have been reassigned on the failed record
777 assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "Time Entries are not assigned to system activities"
800 assert_equal 3, TimeEntry.find_all_by_activity_id_and_project_id(9, 1).size, "Time Entries are not assigned to system activities"
778 # TimeEntries shouldn't have been reassigned on the saved record either
801 # TimeEntries shouldn't have been reassigned on the saved record either
779 assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size, "Time Entries are not assigned to system activities"
802 assert_equal 1, TimeEntry.find_all_by_activity_id_and_project_id(10, 1).size, "Time Entries are not assigned to system activities"
780 end
803 end
781
804
782 # A hook that is manually registered later
805 # A hook that is manually registered later
783 class ProjectBasedTemplate < Redmine::Hook::ViewListener
806 class ProjectBasedTemplate < Redmine::Hook::ViewListener
784 def view_layouts_base_html_head(context)
807 def view_layouts_base_html_head(context)
785 # Adds a project stylesheet
808 # Adds a project stylesheet
786 stylesheet_link_tag(context[:project].identifier) if context[:project]
809 stylesheet_link_tag(context[:project].identifier) if context[:project]
787 end
810 end
788 end
811 end
789 # Don't use this hook now
812 # Don't use this hook now
790 Redmine::Hook.clear_listeners
813 Redmine::Hook.clear_listeners
791
814
792 def test_hook_response
815 def test_hook_response
793 Redmine::Hook.add_listener(ProjectBasedTemplate)
816 Redmine::Hook.add_listener(ProjectBasedTemplate)
794 get :show, :id => 1
817 get :show, :id => 1
795 assert_tag :tag => 'link', :attributes => {:href => '/stylesheets/ecookbook.css'},
818 assert_tag :tag => 'link', :attributes => {:href => '/stylesheets/ecookbook.css'},
796 :parent => {:tag => 'head'}
819 :parent => {:tag => 'head'}
797
820
798 Redmine::Hook.clear_listeners
821 Redmine::Hook.clear_listeners
799 end
822 end
800 end
823 end
General Comments 0
You need to be logged in to leave comments. Login now