##// END OF EJS Templates
Refactor: Split IssuesController#new to #new and #create to match REST pattern....
Eric Davis -
r3574:2c898d8a817e
parent child
Show More
@@ -1,580 +1,601
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class IssuesController < ApplicationController
19 19 menu_item :new_issue, :only => :new
20 20 default_search_scope :issues
21 21
22 22 before_filter :find_issue, :only => [:show, :edit, :update, :reply]
23 23 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
24 before_filter :find_project, :only => [:new, :update_form, :preview, :auto_complete]
24 before_filter :find_project, :only => [:new, :create, :update_form, :preview, :auto_complete]
25 25 before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :context_menu]
26 26 before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
27 27 accept_key_auth :index, :show, :changes
28 28
29 29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
30 30
31 31 helper :journals
32 32 helper :projects
33 33 include ProjectsHelper
34 34 helper :custom_fields
35 35 include CustomFieldsHelper
36 36 helper :issue_relations
37 37 include IssueRelationsHelper
38 38 helper :watchers
39 39 include WatchersHelper
40 40 helper :attachments
41 41 include AttachmentsHelper
42 42 helper :queries
43 43 include QueriesHelper
44 44 helper :sort
45 45 include SortHelper
46 46 include IssuesHelper
47 47 helper :timelog
48 48 include Redmine::Export::PDF
49 49
50 50 verify :method => [:post, :delete],
51 51 :only => :destroy,
52 52 :render => { :nothing => true, :status => :method_not_allowed }
53 53
54 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
54 55 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
55 56
56 57 def index
57 58 retrieve_query
58 59 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
59 60 sort_update(@query.sortable_columns)
60 61
61 62 if @query.valid?
62 63 limit = case params[:format]
63 64 when 'csv', 'pdf'
64 65 Setting.issues_export_limit.to_i
65 66 when 'atom'
66 67 Setting.feeds_limit.to_i
67 68 else
68 69 per_page_option
69 70 end
70 71
71 72 @issue_count = @query.issue_count
72 73 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
73 74 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
74 75 :order => sort_clause,
75 76 :offset => @issue_pages.current.offset,
76 77 :limit => limit)
77 78 @issue_count_by_group = @query.issue_count_by_group
78 79
79 80 respond_to do |format|
80 81 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
81 82 format.xml { render :layout => false }
82 83 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
83 84 format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
84 85 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
85 86 end
86 87 else
87 88 # Send html if the query is not valid
88 89 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
89 90 end
90 91 rescue ActiveRecord::RecordNotFound
91 92 render_404
92 93 end
93 94
94 95 def changes
95 96 retrieve_query
96 97 sort_init 'id', 'desc'
97 98 sort_update(@query.sortable_columns)
98 99
99 100 if @query.valid?
100 101 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
101 102 :limit => 25)
102 103 end
103 104 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
104 105 render :layout => false, :content_type => 'application/atom+xml'
105 106 rescue ActiveRecord::RecordNotFound
106 107 render_404
107 108 end
108 109
109 110 def show
110 111 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
111 112 @journals.each_with_index {|j,i| j.indice = i+1}
112 113 @journals.reverse! if User.current.wants_comments_in_reverse_order?
113 114 @changesets = @issue.changesets.visible.all
114 115 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
115 116 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
116 117 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
117 118 @priorities = IssuePriority.all
118 119 @time_entry = TimeEntry.new
119 120 respond_to do |format|
120 121 format.html { render :template => 'issues/show.rhtml' }
121 122 format.xml { render :layout => false }
122 123 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
123 124 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
124 125 end
125 126 end
126 127
127 128 # Add a new issue
128 129 # The new issue will be created from an existing one if copy_from parameter is given
129 130 def new
130 131 @issue = Issue.new
131 132 @issue.copy_from(params[:copy_from]) if params[:copy_from]
132 133 @issue.project = @project
133 134 # Tracker must be set before custom field values
134 135 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
135 136 if @issue.tracker.nil?
136 137 render_error l(:error_no_tracker_in_project)
137 138 return
138 139 end
139 140 if @issue.status.nil?
140 141 render_error l(:error_no_default_issue_status)
141 142 return
142 143 end
143 144 if params[:issue].is_a?(Hash)
144 145 @issue.safe_attributes = params[:issue]
145 146 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
146 147 end
147 148 @issue.author = User.current
148
149 if request.get? || request.xhr?
150 @issue.start_date ||= Date.today
151 else
152 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
153 if @issue.save
154 attachments = Attachment.attach_files(@issue, params[:attachments])
155 render_attachment_warning_if_needed(@issue)
156 flash[:notice] = l(:notice_successful_create)
157 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
158 respond_to do |format|
159 format.html {
160 redirect_to(params[:continue] ? { :action => 'new', :issue => {:tracker_id => @issue.tracker,
161 :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
162 { :action => 'show', :id => @issue })
163 }
164 format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
165 end
166 return
167 else
168 respond_to do |format|
169 format.html { }
170 format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
171 end
172 end
173 end
149 @issue.start_date ||= Date.today
150 @priorities = IssuePriority.all
151 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
152 render :action => 'new', :layout => !request.xhr?
153 end
154
155 def create
156 @issue = Issue.new
157 @issue.copy_from(params[:copy_from]) if params[:copy_from]
158 @issue.project = @project
159 # Tracker must be set before custom field values
160 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
161 if @issue.tracker.nil?
162 render_error l(:error_no_tracker_in_project)
163 return
164 end
165 if @issue.status.nil?
166 render_error l(:error_no_default_issue_status)
167 return
168 end
169 if params[:issue].is_a?(Hash)
170 @issue.safe_attributes = params[:issue]
171 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
172 end
173 @issue.author = User.current
174
174 175 @priorities = IssuePriority.all
175 176 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
176 render :layout => !request.xhr?
177
178 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
179 if @issue.save
180 attachments = Attachment.attach_files(@issue, params[:attachments])
181 render_attachment_warning_if_needed(@issue)
182 flash[:notice] = l(:notice_successful_create)
183 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
184 respond_to do |format|
185 format.html {
186 redirect_to(params[:continue] ? { :action => 'new', :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
187 { :action => 'show', :id => @issue })
188 }
189 format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
190 end
191 return
192 else
193 respond_to do |format|
194 format.html { render :action => 'new' }
195 format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
196 end
197 end
177 198 end
178 199
179 200 # Attributes that can be updated on workflow transition (without :edit permission)
180 201 # TODO: make it configurable (at least per role)
181 202 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
182 203
183 204 def edit
184 205 update_issue_from_params
185 206
186 207 @journal = @issue.current_journal
187 208
188 209 respond_to do |format|
189 210 format.html { }
190 211 format.xml { }
191 212 end
192 213 end
193 214
194 215 def update
195 216 update_issue_from_params
196 217
197 218 if @issue.save_issue_with_child_records(params, @time_entry)
198 219 render_attachment_warning_if_needed(@issue)
199 220 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
200 221
201 222 respond_to do |format|
202 223 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
203 224 format.xml { head :ok }
204 225 end
205 226 else
206 227 render_attachment_warning_if_needed(@issue)
207 228 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
208 229 @journal = @issue.current_journal
209 230
210 231 respond_to do |format|
211 232 format.html { render :action => 'edit' }
212 233 format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
213 234 end
214 235 end
215 236 end
216 237
217 238 def reply
218 239 journal = Journal.find(params[:journal_id]) if params[:journal_id]
219 240 if journal
220 241 user = journal.user
221 242 text = journal.notes
222 243 else
223 244 user = @issue.author
224 245 text = @issue.description
225 246 end
226 247 # Replaces pre blocks with [...]
227 248 text = text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]')
228 249 content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> "
229 250 content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n"
230 251
231 252 render(:update) { |page|
232 253 page.<< "$('notes').value = \"#{escape_javascript content}\";"
233 254 page.show 'update'
234 255 page << "Form.Element.focus('notes');"
235 256 page << "Element.scrollTo('update');"
236 257 page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
237 258 }
238 259 end
239 260
240 261 # Bulk edit a set of issues
241 262 def bulk_edit
242 263 @issues.sort!
243 264 if request.post?
244 265 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
245 266 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
246 267 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
247 268
248 269 unsaved_issue_ids = []
249 270 @issues.each do |issue|
250 271 issue.reload
251 272 journal = issue.init_journal(User.current, params[:notes])
252 273 issue.safe_attributes = attributes
253 274 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
254 275 unless issue.save
255 276 # Keep unsaved issue ids to display them in flash error
256 277 unsaved_issue_ids << issue.id
257 278 end
258 279 end
259 280 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
260 281 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
261 282 return
262 283 end
263 284 @available_statuses = Workflow.available_statuses(@project)
264 285 @custom_fields = @project.all_issue_custom_fields
265 286 end
266 287
267 288 def move
268 289 @issues.sort!
269 290 @copy = params[:copy_options] && params[:copy_options][:copy]
270 291 @allowed_projects = Issue.allowed_target_projects_on_move
271 292 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
272 293 @target_project ||= @project
273 294 @trackers = @target_project.trackers
274 295 @available_statuses = Workflow.available_statuses(@project)
275 296 if request.post?
276 297 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
277 298 unsaved_issue_ids = []
278 299 moved_issues = []
279 300 @issues.each do |issue|
280 301 issue.reload
281 302 changed_attributes = {}
282 303 [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute|
283 304 unless params[valid_attribute].blank?
284 305 changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
285 306 end
286 307 end
287 308 issue.init_journal(User.current)
288 309 call_hook(:controller_issues_move_before_save, { :params => params, :issue => issue, :target_project => @target_project, :copy => !!@copy })
289 310 if r = issue.move_to_project(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes})
290 311 moved_issues << r
291 312 else
292 313 unsaved_issue_ids << issue.id
293 314 end
294 315 end
295 316 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
296 317
297 318 if params[:follow]
298 319 if @issues.size == 1 && moved_issues.size == 1
299 320 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
300 321 else
301 322 redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project)
302 323 end
303 324 else
304 325 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
305 326 end
306 327 return
307 328 end
308 329 render :layout => false if request.xhr?
309 330 end
310 331
311 332 def destroy
312 333 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
313 334 if @hours > 0
314 335 case params[:todo]
315 336 when 'destroy'
316 337 # nothing to do
317 338 when 'nullify'
318 339 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
319 340 when 'reassign'
320 341 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
321 342 if reassign_to.nil?
322 343 flash.now[:error] = l(:error_issue_not_found_in_project)
323 344 return
324 345 else
325 346 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
326 347 end
327 348 else
328 349 unless params[:format] == 'xml'
329 350 # display the destroy form if it's a user request
330 351 return
331 352 end
332 353 end
333 354 end
334 355 @issues.each(&:destroy)
335 356 respond_to do |format|
336 357 format.html { redirect_to :action => 'index', :project_id => @project }
337 358 format.xml { head :ok }
338 359 end
339 360 end
340 361
341 362 def gantt
342 363 @gantt = Redmine::Helpers::Gantt.new(params)
343 364 retrieve_query
344 365 @query.group_by = nil
345 366 if @query.valid?
346 367 events = []
347 368 # Issues that have start and due dates
348 369 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
349 370 :order => "start_date, due_date",
350 371 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
351 372 )
352 373 # Issues that don't have a due date but that are assigned to a version with a date
353 374 events += @query.issues(:include => [:tracker, :assigned_to, :priority, :fixed_version],
354 375 :order => "start_date, effective_date",
355 376 :conditions => ["(((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
356 377 )
357 378 # Versions
358 379 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
359 380
360 381 @gantt.events = events
361 382 end
362 383
363 384 basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
364 385
365 386 respond_to do |format|
366 387 format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
367 388 format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
368 389 format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
369 390 end
370 391 end
371 392
372 393 def calendar
373 394 if params[:year] and params[:year].to_i > 1900
374 395 @year = params[:year].to_i
375 396 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
376 397 @month = params[:month].to_i
377 398 end
378 399 end
379 400 @year ||= Date.today.year
380 401 @month ||= Date.today.month
381 402
382 403 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
383 404 retrieve_query
384 405 @query.group_by = nil
385 406 if @query.valid?
386 407 events = []
387 408 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
388 409 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
389 410 )
390 411 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
391 412
392 413 @calendar.events = events
393 414 end
394 415
395 416 render :layout => false if request.xhr?
396 417 end
397 418
398 419 def context_menu
399 420 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
400 421 if (@issues.size == 1)
401 422 @issue = @issues.first
402 423 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
403 424 end
404 425 projects = @issues.collect(&:project).compact.uniq
405 426 @project = projects.first if projects.size == 1
406 427
407 428 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
408 429 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
409 430 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
410 431 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
411 432 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
412 433 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
413 434 }
414 435 if @project
415 436 @assignables = @project.assignable_users
416 437 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
417 438 @trackers = @project.trackers
418 439 end
419 440
420 441 @priorities = IssuePriority.all.reverse
421 442 @statuses = IssueStatus.find(:all, :order => 'position')
422 443 @back = params[:back_url] || request.env['HTTP_REFERER']
423 444
424 445 render :layout => false
425 446 end
426 447
427 448 def update_form
428 449 if params[:id].blank?
429 450 @issue = Issue.new
430 451 @issue.project = @project
431 452 else
432 453 @issue = @project.issues.visible.find(params[:id])
433 454 end
434 455 @issue.attributes = params[:issue]
435 456 @allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
436 457 @priorities = IssuePriority.all
437 458
438 459 render :partial => 'attributes'
439 460 end
440 461
441 462 def preview
442 463 @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
443 464 if @issue
444 465 @attachements = @issue.attachments
445 466 @description = params[:issue] && params[:issue][:description]
446 467 if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n")
447 468 @description = nil
448 469 end
449 470 @notes = params[:notes]
450 471 else
451 472 @description = (params[:issue] ? params[:issue][:description] : nil)
452 473 end
453 474 render :layout => false
454 475 end
455 476
456 477 def auto_complete
457 478 @issues = []
458 479 q = params[:q].to_s
459 480 if q.match(/^\d+$/)
460 481 @issues << @project.issues.visible.find_by_id(q.to_i)
461 482 end
462 483 unless q.blank?
463 484 @issues += @project.issues.visible.find(:all, :conditions => ["LOWER(#{Issue.table_name}.subject) LIKE ?", "%#{q.downcase}%"], :limit => 10)
464 485 end
465 486 render :layout => false
466 487 end
467 488
468 489 private
469 490 def find_issue
470 491 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
471 492 @project = @issue.project
472 493 rescue ActiveRecord::RecordNotFound
473 494 render_404
474 495 end
475 496
476 497 # Filter for bulk operations
477 498 def find_issues
478 499 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
479 500 raise ActiveRecord::RecordNotFound if @issues.empty?
480 501 projects = @issues.collect(&:project).compact.uniq
481 502 if projects.size == 1
482 503 @project = projects.first
483 504 else
484 505 # TODO: let users bulk edit/move/destroy issues from different projects
485 506 render_error 'Can not bulk edit/move/destroy issues from different projects'
486 507 return false
487 508 end
488 509 rescue ActiveRecord::RecordNotFound
489 510 render_404
490 511 end
491 512
492 513 def find_project
493 514 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
494 515 @project = Project.find(project_id)
495 516 rescue ActiveRecord::RecordNotFound
496 517 render_404
497 518 end
498 519
499 520 def find_optional_project
500 521 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
501 522 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
502 523 allowed ? true : deny_access
503 524 rescue ActiveRecord::RecordNotFound
504 525 render_404
505 526 end
506 527
507 528 # Retrieve query from session or build a new query
508 529 def retrieve_query
509 530 if !params[:query_id].blank?
510 531 cond = "project_id IS NULL"
511 532 cond << " OR project_id = #{@project.id}" if @project
512 533 @query = Query.find(params[:query_id], :conditions => cond)
513 534 @query.project = @project
514 535 session[:query] = {:id => @query.id, :project_id => @query.project_id}
515 536 sort_clear
516 537 else
517 538 if api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
518 539 # Give it a name, required to be valid
519 540 @query = Query.new(:name => "_")
520 541 @query.project = @project
521 542 if params[:fields] and params[:fields].is_a? Array
522 543 params[:fields].each do |field|
523 544 @query.add_filter(field, params[:operators][field], params[:values][field])
524 545 end
525 546 else
526 547 @query.available_filters.keys.each do |field|
527 548 @query.add_short_filter(field, params[field]) if params[field]
528 549 end
529 550 end
530 551 @query.group_by = params[:group_by]
531 552 @query.column_names = params[:query] && params[:query][:column_names]
532 553 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
533 554 else
534 555 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
535 556 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
536 557 @query.project = @project
537 558 end
538 559 end
539 560 end
540 561
541 562 # Rescues an invalid query statement. Just in case...
542 563 def query_statement_invalid(exception)
543 564 logger.error "Query::StatementInvalid: #{exception.message}" if logger
544 565 session.delete(:query)
545 566 sort_clear
546 567 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
547 568 end
548 569
549 570 # Used by #edit and #update to set some common instance variables
550 571 # from the params
551 572 # TODO: Refactor, not everything in here is needed by #edit
552 573 def update_issue_from_params
553 574 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
554 575 @priorities = IssuePriority.all
555 576 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
556 577 @time_entry = TimeEntry.new
557 578
558 579 @notes = params[:notes]
559 580 @issue.init_journal(User.current, @notes)
560 581 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
561 582 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
562 583 attrs = params[:issue].dup
563 584 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
564 585 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
565 586 @issue.safe_attributes = attrs
566 587 end
567 588
568 589 end
569 590
570 591 def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
571 592 if unsaved_issue_ids.empty?
572 593 flash[:notice] = l(:notice_successful_update) unless issues.empty?
573 594 else
574 595 flash[:error] = l(:notice_failed_to_save_issues,
575 596 :count => unsaved_issue_ids.size,
576 597 :total => issues.size,
577 598 :ids => '#' + unsaved_issue_ids.join(', #'))
578 599 end
579 600 end
580 601 end
@@ -1,26 +1,26
1 1 <h2><%=l(:label_issue_new)%></h2>
2 2
3 <% labelled_tabular_form_for :issue, @issue,
3 <% labelled_tabular_form_for :issue, @issue, :url => {:controller => 'issues', :action => 'create', :project_id => @project},
4 4 :html => {:multipart => true, :id => 'issue-form'} do |f| %>
5 5 <%= error_messages_for 'issue' %>
6 6 <div class="box">
7 7 <%= render :partial => 'issues/form', :locals => {:f => f} %>
8 8 </div>
9 9 <%= submit_tag l(:button_create) %>
10 10 <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
11 11 <%= link_to_remote l(:label_preview),
12 12 { :url => { :controller => 'issues', :action => 'preview', :project_id => @project },
13 13 :method => 'post',
14 14 :update => 'preview',
15 15 :with => "Form.serialize('issue-form')",
16 16 :complete => "Element.scrollTo('preview')"
17 17 }, :accesskey => accesskey(:preview) %>
18 18
19 19 <%= javascript_tag "Form.Element.focus('issue_subject');" %>
20 20 <% end %>
21 21
22 22 <div id="preview" class="wiki"></div>
23 23
24 24 <% content_for :header_tags do %>
25 25 <%= stylesheet_link_tag 'scm' %>
26 26 <% end %>
@@ -1,289 +1,289
1 1 ActionController::Routing::Routes.draw do |map|
2 2 # Add your own custom routes here.
3 3 # The priority is based upon order of creation: first created -> highest priority.
4 4
5 5 # Here's a sample route:
6 6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
7 7 # Keep in mind you can assign values other than :controller and :action
8 8
9 9 map.home '', :controller => 'welcome'
10 10
11 11 map.signin 'login', :controller => 'account', :action => 'login'
12 12 map.signout 'logout', :controller => 'account', :action => 'logout'
13 13
14 14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
15 15 map.connect 'help/:ctrl/:page', :controller => 'help'
16 16
17 17 map.connect 'time_entries/:id/edit', :action => 'edit', :controller => 'timelog'
18 18 map.connect 'projects/:project_id/time_entries/new', :action => 'edit', :controller => 'timelog'
19 19 map.connect 'projects/:project_id/issues/:issue_id/time_entries/new', :action => 'edit', :controller => 'timelog'
20 20
21 21 map.with_options :controller => 'timelog' do |timelog|
22 22 timelog.connect 'projects/:project_id/time_entries', :action => 'details'
23 23
24 24 timelog.with_options :action => 'details', :conditions => {:method => :get} do |time_details|
25 25 time_details.connect 'time_entries'
26 26 time_details.connect 'time_entries.:format'
27 27 time_details.connect 'issues/:issue_id/time_entries'
28 28 time_details.connect 'issues/:issue_id/time_entries.:format'
29 29 time_details.connect 'projects/:project_id/time_entries.:format'
30 30 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries'
31 31 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries.:format'
32 32 end
33 33 timelog.connect 'projects/:project_id/time_entries/report', :action => 'report'
34 34 timelog.with_options :action => 'report',:conditions => {:method => :get} do |time_report|
35 35 time_report.connect 'time_entries/report'
36 36 time_report.connect 'time_entries/report.:format'
37 37 time_report.connect 'projects/:project_id/time_entries/report.:format'
38 38 end
39 39
40 40 timelog.with_options :action => 'edit', :conditions => {:method => :get} do |time_edit|
41 41 time_edit.connect 'issues/:issue_id/time_entries/new'
42 42 end
43 43
44 44 timelog.connect 'time_entries/:id/destroy', :action => 'destroy', :conditions => {:method => :post}
45 45 end
46 46
47 47 map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
48 48 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
49 49 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
50 50 map.with_options :controller => 'wiki' do |wiki_routes|
51 51 wiki_routes.with_options :conditions => {:method => :get} do |wiki_views|
52 52 wiki_views.connect 'projects/:id/wiki/:page', :action => 'special', :page => /page_index|date_index|export/i
53 53 wiki_views.connect 'projects/:id/wiki/:page', :action => 'index', :page => nil
54 54 wiki_views.connect 'projects/:id/wiki/:page/edit', :action => 'edit'
55 55 wiki_views.connect 'projects/:id/wiki/:page/rename', :action => 'rename'
56 56 wiki_views.connect 'projects/:id/wiki/:page/history', :action => 'history'
57 57 wiki_views.connect 'projects/:id/wiki/:page/diff/:version/vs/:version_from', :action => 'diff'
58 58 wiki_views.connect 'projects/:id/wiki/:page/annotate/:version', :action => 'annotate'
59 59 end
60 60
61 61 wiki_routes.connect 'projects/:id/wiki/:page/:action',
62 62 :action => /edit|rename|destroy|preview|protect/,
63 63 :conditions => {:method => :post}
64 64 end
65 65
66 66 map.with_options :controller => 'messages' do |messages_routes|
67 67 messages_routes.with_options :conditions => {:method => :get} do |messages_views|
68 68 messages_views.connect 'boards/:board_id/topics/new', :action => 'new'
69 69 messages_views.connect 'boards/:board_id/topics/:id', :action => 'show'
70 70 messages_views.connect 'boards/:board_id/topics/:id/edit', :action => 'edit'
71 71 end
72 72 messages_routes.with_options :conditions => {:method => :post} do |messages_actions|
73 73 messages_actions.connect 'boards/:board_id/topics/new', :action => 'new'
74 74 messages_actions.connect 'boards/:board_id/topics/:id/replies', :action => 'reply'
75 75 messages_actions.connect 'boards/:board_id/topics/:id/:action', :action => /edit|destroy/
76 76 end
77 77 end
78 78
79 79 map.with_options :controller => 'boards' do |board_routes|
80 80 board_routes.with_options :conditions => {:method => :get} do |board_views|
81 81 board_views.connect 'projects/:project_id/boards', :action => 'index'
82 82 board_views.connect 'projects/:project_id/boards/new', :action => 'new'
83 83 board_views.connect 'projects/:project_id/boards/:id', :action => 'show'
84 84 board_views.connect 'projects/:project_id/boards/:id.:format', :action => 'show'
85 85 board_views.connect 'projects/:project_id/boards/:id/edit', :action => 'edit'
86 86 end
87 87 board_routes.with_options :conditions => {:method => :post} do |board_actions|
88 88 board_actions.connect 'projects/:project_id/boards', :action => 'new'
89 89 board_actions.connect 'projects/:project_id/boards/:id/:action', :action => /edit|destroy/
90 90 end
91 91 end
92 92
93 93 map.with_options :controller => 'documents' do |document_routes|
94 94 document_routes.with_options :conditions => {:method => :get} do |document_views|
95 95 document_views.connect 'projects/:project_id/documents', :action => 'index'
96 96 document_views.connect 'projects/:project_id/documents/new', :action => 'new'
97 97 document_views.connect 'documents/:id', :action => 'show'
98 98 document_views.connect 'documents/:id/edit', :action => 'edit'
99 99 end
100 100 document_routes.with_options :conditions => {:method => :post} do |document_actions|
101 101 document_actions.connect 'projects/:project_id/documents', :action => 'new'
102 102 document_actions.connect 'documents/:id/:action', :action => /destroy|edit/
103 103 end
104 104 end
105 105
106 106 map.with_options :controller => 'issues' do |issues_routes|
107 107 issues_routes.with_options :conditions => {:method => :get} do |issues_views|
108 108 issues_views.connect 'issues', :action => 'index'
109 109 issues_views.connect 'issues.:format', :action => 'index'
110 110 issues_views.connect 'projects/:project_id/issues', :action => 'index'
111 111 issues_views.connect 'projects/:project_id/issues.:format', :action => 'index'
112 112 issues_views.connect 'projects/:project_id/issues/new', :action => 'new'
113 113 issues_views.connect 'projects/:project_id/issues/gantt', :action => 'gantt'
114 114 issues_views.connect 'projects/:project_id/issues/calendar', :action => 'calendar'
115 115 issues_views.connect 'projects/:project_id/issues/:copy_from/copy', :action => 'new'
116 116 issues_views.connect 'issues/:id', :action => 'show', :id => /\d+/
117 117 issues_views.connect 'issues/:id.:format', :action => 'show', :id => /\d+/
118 118 issues_views.connect 'issues/:id/edit', :action => 'edit', :id => /\d+/
119 119 issues_views.connect 'issues/:id/move', :action => 'move', :id => /\d+/
120 120 end
121 121 issues_routes.with_options :conditions => {:method => :post} do |issues_actions|
122 122 issues_actions.connect 'issues', :action => 'index'
123 issues_actions.connect 'projects/:project_id/issues', :action => 'new'
123 issues_actions.connect 'projects/:project_id/issues', :action => 'create'
124 124 issues_actions.connect 'issues/:id/quoted', :action => 'reply', :id => /\d+/
125 125 issues_actions.connect 'issues/:id/:action', :action => /edit|move|destroy/, :id => /\d+/
126 issues_actions.connect 'issues.:format', :action => 'new', :format => /xml/
126 issues_actions.connect 'issues.:format', :action => 'create', :format => /xml/
127 127 end
128 128 issues_routes.with_options :conditions => {:method => :put} do |issues_actions|
129 129 issues_actions.connect 'issues/:id/edit', :action => 'update', :id => /\d+/
130 130 issues_actions.connect 'issues/:id.:format', :action => 'update', :id => /\d+/, :format => /xml/
131 131 end
132 132 issues_routes.with_options :conditions => {:method => :delete} do |issues_actions|
133 133 issues_actions.connect 'issues/:id.:format', :action => 'destroy', :id => /\d+/, :format => /xml/
134 134 end
135 135 issues_routes.connect 'issues/:action'
136 136 end
137 137
138 138 map.with_options :controller => 'issue_relations', :conditions => {:method => :post} do |relations|
139 139 relations.connect 'issues/:issue_id/relations/:id', :action => 'new'
140 140 relations.connect 'issues/:issue_id/relations/:id/destroy', :action => 'destroy'
141 141 end
142 142
143 143 map.with_options :controller => 'reports', :conditions => {:method => :get} do |reports|
144 144 reports.connect 'projects/:id/issues/report', :action => 'issue_report'
145 145 reports.connect 'projects/:id/issues/report/:detail', :action => 'issue_report_details'
146 146 end
147 147
148 148 map.with_options :controller => 'news' do |news_routes|
149 149 news_routes.with_options :conditions => {:method => :get} do |news_views|
150 150 news_views.connect 'news', :action => 'index'
151 151 news_views.connect 'projects/:project_id/news', :action => 'index'
152 152 news_views.connect 'projects/:project_id/news.:format', :action => 'index'
153 153 news_views.connect 'news.:format', :action => 'index'
154 154 news_views.connect 'projects/:project_id/news/new', :action => 'new'
155 155 news_views.connect 'news/:id', :action => 'show'
156 156 news_views.connect 'news/:id/edit', :action => 'edit'
157 157 end
158 158 news_routes.with_options do |news_actions|
159 159 news_actions.connect 'projects/:project_id/news', :action => 'new'
160 160 news_actions.connect 'news/:id/edit', :action => 'edit'
161 161 news_actions.connect 'news/:id/destroy', :action => 'destroy'
162 162 end
163 163 end
164 164
165 165 map.connect 'projects/:id/members/new', :controller => 'members', :action => 'new'
166 166
167 167 map.with_options :controller => 'users' do |users|
168 168 users.with_options :conditions => {:method => :get} do |user_views|
169 169 user_views.connect 'users', :action => 'index'
170 170 user_views.connect 'users/:id', :action => 'show', :id => /\d+/
171 171 user_views.connect 'users/new', :action => 'add'
172 172 user_views.connect 'users/:id/edit/:tab', :action => 'edit', :tab => nil
173 173 end
174 174 users.with_options :conditions => {:method => :post} do |user_actions|
175 175 user_actions.connect 'users', :action => 'add'
176 176 user_actions.connect 'users/new', :action => 'add'
177 177 user_actions.connect 'users/:id/edit', :action => 'edit'
178 178 user_actions.connect 'users/:id/memberships', :action => 'edit_membership'
179 179 user_actions.connect 'users/:id/memberships/:membership_id', :action => 'edit_membership'
180 180 user_actions.connect 'users/:id/memberships/:membership_id/destroy', :action => 'destroy_membership'
181 181 end
182 182 end
183 183
184 184 map.with_options :controller => 'projects' do |projects|
185 185 projects.with_options :conditions => {:method => :get} do |project_views|
186 186 project_views.connect 'projects', :action => 'index'
187 187 project_views.connect 'projects.:format', :action => 'index'
188 188 project_views.connect 'projects/new', :action => 'add'
189 189 project_views.connect 'projects/:id', :action => 'show'
190 190 project_views.connect 'projects/:id.:format', :action => 'show'
191 191 project_views.connect 'projects/:id/:action', :action => /roadmap|destroy|settings/
192 192 project_views.connect 'projects/:id/files', :action => 'list_files'
193 193 project_views.connect 'projects/:id/files/new', :action => 'add_file'
194 194 project_views.connect 'projects/:id/settings/:tab', :action => 'settings'
195 195 end
196 196
197 197 projects.with_options :action => 'activity', :conditions => {:method => :get} do |activity|
198 198 activity.connect 'projects/:id/activity'
199 199 activity.connect 'projects/:id/activity.:format'
200 200 activity.connect 'activity', :id => nil
201 201 activity.connect 'activity.:format', :id => nil
202 202 end
203 203
204 204 projects.with_options :conditions => {:method => :post} do |project_actions|
205 205 project_actions.connect 'projects/new', :action => 'add'
206 206 project_actions.connect 'projects', :action => 'add'
207 207 project_actions.connect 'projects.:format', :action => 'add', :format => /xml/
208 208 project_actions.connect 'projects/:id/:action', :action => /edit|destroy|archive|unarchive/
209 209 project_actions.connect 'projects/:id/files/new', :action => 'add_file'
210 210 project_actions.connect 'projects/:id/activities/save', :action => 'save_activities'
211 211 end
212 212
213 213 projects.with_options :conditions => {:method => :put} do |project_actions|
214 214 project_actions.conditions 'projects/:id.:format', :action => 'edit', :format => /xml/
215 215 end
216 216
217 217 projects.with_options :conditions => {:method => :delete} do |project_actions|
218 218 project_actions.conditions 'projects/:id.:format', :action => 'destroy', :format => /xml/
219 219 project_actions.conditions 'projects/:id/reset_activities', :action => 'reset_activities'
220 220 end
221 221 end
222 222
223 223 map.with_options :controller => 'versions' do |versions|
224 224 versions.connect 'projects/:project_id/versions/new', :action => 'new'
225 225 versions.with_options :conditions => {:method => :post} do |version_actions|
226 226 version_actions.connect 'projects/:project_id/versions/close_completed', :action => 'close_completed'
227 227 end
228 228 end
229 229
230 230 map.with_options :controller => 'issue_categories' do |categories|
231 231 categories.connect 'projects/:project_id/issue_categories/new', :action => 'new'
232 232 end
233 233
234 234 map.with_options :controller => 'repositories' do |repositories|
235 235 repositories.with_options :conditions => {:method => :get} do |repository_views|
236 236 repository_views.connect 'projects/:id/repository', :action => 'show'
237 237 repository_views.connect 'projects/:id/repository/edit', :action => 'edit'
238 238 repository_views.connect 'projects/:id/repository/statistics', :action => 'stats'
239 239 repository_views.connect 'projects/:id/repository/revisions', :action => 'revisions'
240 240 repository_views.connect 'projects/:id/repository/revisions.:format', :action => 'revisions'
241 241 repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
242 242 repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
243 243 repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
244 244 repository_views.connect 'projects/:id/repository/revisions/:rev/raw/*path', :action => 'entry', :format => 'raw', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
245 245 repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
246 246 repository_views.connect 'projects/:id/repository/raw/*path', :action => 'entry', :format => 'raw'
247 247 # TODO: why the following route is required?
248 248 repository_views.connect 'projects/:id/repository/entry/*path', :action => 'entry'
249 249 repository_views.connect 'projects/:id/repository/:action/*path'
250 250 end
251 251
252 252 repositories.connect 'projects/:id/repository/:action', :conditions => {:method => :post}
253 253 end
254 254
255 255 map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
256 256 map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
257 257 map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
258 258
259 259 map.resources :groups
260 260
261 261 #left old routes at the bottom for backwards compat
262 262 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
263 263 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
264 264 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
265 265 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
266 266 map.connect 'wiki/:id/:page/:action', :page => nil, :controller => 'wiki'
267 267 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
268 268 map.connect 'projects/:project_id/news/:action', :controller => 'news'
269 269 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
270 270 map.with_options :controller => 'repositories' do |omap|
271 271 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
272 272 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
273 273 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
274 274 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
275 275 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
276 276 omap.connect 'repositories/revision/:id/:rev', :action => 'revision'
277 277 end
278 278
279 279 map.with_options :controller => 'sys' do |sys|
280 280 sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get}
281 281 sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post}
282 282 end
283 283
284 284 # Install the default route as the lowest priority.
285 285 map.connect ':controller/:action/:id'
286 286 map.connect 'robots.txt', :controller => 'welcome', :action => 'robots'
287 287 # Used for OpenID
288 288 map.root :controller => 'account', :action => 'login'
289 289 end
@@ -1,206 +1,206
1 1 require 'redmine/access_control'
2 2 require 'redmine/menu_manager'
3 3 require 'redmine/activity'
4 4 require 'redmine/search'
5 5 require 'redmine/custom_field_format'
6 6 require 'redmine/mime_type'
7 7 require 'redmine/core_ext'
8 8 require 'redmine/themes'
9 9 require 'redmine/hook'
10 10 require 'redmine/plugin'
11 11 require 'redmine/wiki_formatting'
12 12 require 'redmine/scm/base'
13 13
14 14 begin
15 15 require_library_or_gem 'RMagick' unless Object.const_defined?(:Magick)
16 16 rescue LoadError
17 17 # RMagick is not available
18 18 end
19 19
20 20 if RUBY_VERSION < '1.9'
21 21 require 'faster_csv'
22 22 else
23 23 require 'csv'
24 24 FCSV = CSV
25 25 end
26 26
27 27 Redmine::Scm::Base.add "Subversion"
28 28 Redmine::Scm::Base.add "Darcs"
29 29 Redmine::Scm::Base.add "Mercurial"
30 30 Redmine::Scm::Base.add "Cvs"
31 31 Redmine::Scm::Base.add "Bazaar"
32 32 Redmine::Scm::Base.add "Git"
33 33 Redmine::Scm::Base.add "Filesystem"
34 34
35 35 Redmine::CustomFieldFormat.map do |fields|
36 36 fields.register Redmine::CustomFieldFormat.new('string', :label => :label_string, :order => 1)
37 37 fields.register Redmine::CustomFieldFormat.new('text', :label => :label_text, :order => 2)
38 38 fields.register Redmine::CustomFieldFormat.new('int', :label => :label_integer, :order => 3)
39 39 fields.register Redmine::CustomFieldFormat.new('float', :label => :label_float, :order => 4)
40 40 fields.register Redmine::CustomFieldFormat.new('list', :label => :label_list, :order => 5)
41 41 fields.register Redmine::CustomFieldFormat.new('date', :label => :label_date, :order => 6)
42 42 fields.register Redmine::CustomFieldFormat.new('bool', :label => :label_boolean, :order => 7)
43 43 end
44 44
45 45 # Permissions
46 46 Redmine::AccessControl.map do |map|
47 47 map.permission :view_project, {:projects => [:show, :activity]}, :public => true
48 48 map.permission :search_project, {:search => :index}, :public => true
49 49 map.permission :add_project, {:projects => :add}, :require => :loggedin
50 50 map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member
51 51 map.permission :select_project_modules, {:projects => :modules}, :require => :member
52 52 map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy, :autocomplete_for_member]}, :require => :member
53 53 map.permission :manage_versions, {:projects => :settings, :versions => [:new, :edit, :close_completed, :destroy]}, :require => :member
54 54 map.permission :add_subprojects, {:projects => :add}, :require => :member
55 55
56 56 map.project_module :issue_tracking do |map|
57 57 # Issue categories
58 58 map.permission :manage_categories, {:projects => :settings, :issue_categories => [:new, :edit, :destroy]}, :require => :member
59 59 # Issues
60 60 map.permission :view_issues, {:projects => :roadmap,
61 61 :issues => [:index, :changes, :show, :context_menu, :auto_complete],
62 62 :versions => [:show, :status_by],
63 63 :queries => :index,
64 64 :reports => [:issue_report, :issue_report_details]}
65 map.permission :add_issues, {:issues => [:new, :update_form]}
65 map.permission :add_issues, {:issues => [:new, :create, :update_form]}
66 66 map.permission :edit_issues, {:issues => [:edit, :update, :reply, :bulk_edit, :update_form]}
67 67 map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
68 68 map.permission :manage_subtasks, {}
69 69 map.permission :add_issue_notes, {:issues => [:edit, :update, :reply]}
70 70 map.permission :edit_issue_notes, {:journals => :edit}, :require => :loggedin
71 71 map.permission :edit_own_issue_notes, {:journals => :edit}, :require => :loggedin
72 72 map.permission :move_issues, {:issues => :move}, :require => :loggedin
73 73 map.permission :delete_issues, {:issues => :destroy}, :require => :member
74 74 # Queries
75 75 map.permission :manage_public_queries, {:queries => [:new, :edit, :destroy]}, :require => :member
76 76 map.permission :save_queries, {:queries => [:new, :edit, :destroy]}, :require => :loggedin
77 77 # Gantt & calendar
78 78 map.permission :view_gantt, :issues => :gantt
79 79 map.permission :view_calendar, :issues => :calendar
80 80 # Watchers
81 81 map.permission :view_issue_watchers, {}
82 82 map.permission :add_issue_watchers, {:watchers => :new}
83 83 map.permission :delete_issue_watchers, {:watchers => :destroy}
84 84 end
85 85
86 86 map.project_module :time_tracking do |map|
87 87 map.permission :log_time, {:timelog => :edit}, :require => :loggedin
88 88 map.permission :view_time_entries, :timelog => [:details, :report]
89 89 map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member
90 90 map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin
91 91 map.permission :manage_project_activities, {:projects => [:save_activities, :reset_activities]}, :require => :member
92 92 end
93 93
94 94 map.project_module :news do |map|
95 95 map.permission :manage_news, {:news => [:new, :edit, :destroy, :destroy_comment]}, :require => :member
96 96 map.permission :view_news, {:news => [:index, :show]}, :public => true
97 97 map.permission :comment_news, {:news => :add_comment}
98 98 end
99 99
100 100 map.project_module :documents do |map|
101 101 map.permission :manage_documents, {:documents => [:new, :edit, :destroy, :add_attachment]}, :require => :loggedin
102 102 map.permission :view_documents, :documents => [:index, :show, :download]
103 103 end
104 104
105 105 map.project_module :files do |map|
106 106 map.permission :manage_files, {:projects => :add_file}, :require => :loggedin
107 107 map.permission :view_files, :projects => :list_files, :versions => :download
108 108 end
109 109
110 110 map.project_module :wiki do |map|
111 111 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
112 112 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
113 113 map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
114 114 map.permission :view_wiki_pages, :wiki => [:index, :special]
115 115 map.permission :export_wiki_pages, {}
116 116 map.permission :view_wiki_edits, :wiki => [:history, :diff, :annotate]
117 117 map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment]
118 118 map.permission :delete_wiki_pages_attachments, {}
119 119 map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member
120 120 end
121 121
122 122 map.project_module :repository do |map|
123 123 map.permission :manage_repository, {:repositories => [:edit, :committers, :destroy]}, :require => :member
124 124 map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph]
125 125 map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
126 126 map.permission :commit_access, {}
127 127 end
128 128
129 129 map.project_module :boards do |map|
130 130 map.permission :manage_boards, {:boards => [:new, :edit, :destroy]}, :require => :member
131 131 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true
132 132 map.permission :add_messages, {:messages => [:new, :reply, :quote]}
133 133 map.permission :edit_messages, {:messages => :edit}, :require => :member
134 134 map.permission :edit_own_messages, {:messages => :edit}, :require => :loggedin
135 135 map.permission :delete_messages, {:messages => :destroy}, :require => :member
136 136 map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin
137 137 end
138 138 end
139 139
140 140 Redmine::MenuManager.map :top_menu do |menu|
141 141 menu.push :home, :home_path
142 142 menu.push :my_page, { :controller => 'my', :action => 'page' }, :if => Proc.new { User.current.logged? }
143 143 menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural
144 144 menu.push :administration, { :controller => 'admin', :action => 'index' }, :if => Proc.new { User.current.admin? }, :last => true
145 145 menu.push :help, Redmine::Info.help_url, :last => true
146 146 end
147 147
148 148 Redmine::MenuManager.map :account_menu do |menu|
149 149 menu.push :login, :signin_path, :if => Proc.new { !User.current.logged? }
150 150 menu.push :register, { :controller => 'account', :action => 'register' }, :if => Proc.new { !User.current.logged? && Setting.self_registration? }
151 151 menu.push :my_account, { :controller => 'my', :action => 'account' }, :if => Proc.new { User.current.logged? }
152 152 menu.push :logout, :signout_path, :if => Proc.new { User.current.logged? }
153 153 end
154 154
155 155 Redmine::MenuManager.map :application_menu do |menu|
156 156 # Empty
157 157 end
158 158
159 159 Redmine::MenuManager.map :admin_menu do |menu|
160 160 # Empty
161 161 end
162 162
163 163 Redmine::MenuManager.map :project_menu do |menu|
164 164 menu.push :overview, { :controller => 'projects', :action => 'show' }
165 165 menu.push :activity, { :controller => 'projects', :action => 'activity' }
166 166 menu.push :roadmap, { :controller => 'projects', :action => 'roadmap' },
167 167 :if => Proc.new { |p| p.shared_versions.any? }
168 168 menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
169 169 menu.push :new_issue, { :controller => 'issues', :action => 'new' }, :param => :project_id, :caption => :label_issue_new,
170 170 :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) }
171 171 menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
172 172 menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
173 173 menu.push :wiki, { :controller => 'wiki', :action => 'index', :page => nil },
174 174 :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
175 175 menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
176 176 :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
177 177 menu.push :files, { :controller => 'projects', :action => 'list_files' }, :caption => :label_file_plural
178 178 menu.push :repository, { :controller => 'repositories', :action => 'show' },
179 179 :if => Proc.new { |p| p.repository && !p.repository.new_record? }
180 180 menu.push :settings, { :controller => 'projects', :action => 'settings' }, :last => true
181 181 end
182 182
183 183 Redmine::Activity.map do |activity|
184 184 activity.register :issues, :class_name => %w(Issue Journal)
185 185 activity.register :changesets
186 186 activity.register :news
187 187 activity.register :documents, :class_name => %w(Document Attachment)
188 188 activity.register :files, :class_name => 'Attachment'
189 189 activity.register :wiki_edits, :class_name => 'WikiContent::Version', :default => false
190 190 activity.register :messages, :default => false
191 191 activity.register :time_entries, :default => false
192 192 end
193 193
194 194 Redmine::Search.map do |search|
195 195 search.register :issues
196 196 search.register :news
197 197 search.register :documents
198 198 search.register :changesets
199 199 search.register :wiki_pages
200 200 search.register :messages
201 201 search.register :projects
202 202 end
203 203
204 204 Redmine::WikiFormatting.map do |format|
205 205 format.register :textile, Redmine::WikiFormatting::Textile::Formatter, Redmine::WikiFormatting::Textile::Helper
206 206 end
@@ -1,1354 +1,1354
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19 require 'issues_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class IssuesController; def rescue_action(e) raise e end; end
23 23
24 24 class IssuesControllerTest < ActionController::TestCase
25 25 fixtures :projects,
26 26 :users,
27 27 :roles,
28 28 :members,
29 29 :member_roles,
30 30 :issues,
31 31 :issue_statuses,
32 32 :versions,
33 33 :trackers,
34 34 :projects_trackers,
35 35 :issue_categories,
36 36 :enabled_modules,
37 37 :enumerations,
38 38 :attachments,
39 39 :workflows,
40 40 :custom_fields,
41 41 :custom_values,
42 42 :custom_fields_projects,
43 43 :custom_fields_trackers,
44 44 :time_entries,
45 45 :journals,
46 46 :journal_details,
47 47 :queries
48 48
49 49 def setup
50 50 @controller = IssuesController.new
51 51 @request = ActionController::TestRequest.new
52 52 @response = ActionController::TestResponse.new
53 53 User.current = nil
54 54 end
55 55
56 56 def test_index
57 57 Setting.default_language = 'en'
58 58
59 59 get :index
60 60 assert_response :success
61 61 assert_template 'index.rhtml'
62 62 assert_not_nil assigns(:issues)
63 63 assert_nil assigns(:project)
64 64 assert_tag :tag => 'a', :content => /Can't print recipes/
65 65 assert_tag :tag => 'a', :content => /Subproject issue/
66 66 # private projects hidden
67 67 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
68 68 assert_no_tag :tag => 'a', :content => /Issue on project 2/
69 69 # project column
70 70 assert_tag :tag => 'th', :content => /Project/
71 71 end
72 72
73 73 def test_index_should_not_list_issues_when_module_disabled
74 74 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
75 75 get :index
76 76 assert_response :success
77 77 assert_template 'index.rhtml'
78 78 assert_not_nil assigns(:issues)
79 79 assert_nil assigns(:project)
80 80 assert_no_tag :tag => 'a', :content => /Can't print recipes/
81 81 assert_tag :tag => 'a', :content => /Subproject issue/
82 82 end
83 83
84 84 def test_index_should_not_list_issues_when_module_disabled
85 85 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
86 86 get :index
87 87 assert_response :success
88 88 assert_template 'index.rhtml'
89 89 assert_not_nil assigns(:issues)
90 90 assert_nil assigns(:project)
91 91 assert_no_tag :tag => 'a', :content => /Can't print recipes/
92 92 assert_tag :tag => 'a', :content => /Subproject issue/
93 93 end
94 94
95 95 def test_index_with_project
96 96 Setting.display_subprojects_issues = 0
97 97 get :index, :project_id => 1
98 98 assert_response :success
99 99 assert_template 'index.rhtml'
100 100 assert_not_nil assigns(:issues)
101 101 assert_tag :tag => 'a', :content => /Can't print recipes/
102 102 assert_no_tag :tag => 'a', :content => /Subproject issue/
103 103 end
104 104
105 105 def test_index_with_project_and_subprojects
106 106 Setting.display_subprojects_issues = 1
107 107 get :index, :project_id => 1
108 108 assert_response :success
109 109 assert_template 'index.rhtml'
110 110 assert_not_nil assigns(:issues)
111 111 assert_tag :tag => 'a', :content => /Can't print recipes/
112 112 assert_tag :tag => 'a', :content => /Subproject issue/
113 113 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
114 114 end
115 115
116 116 def test_index_with_project_and_subprojects_should_show_private_subprojects
117 117 @request.session[:user_id] = 2
118 118 Setting.display_subprojects_issues = 1
119 119 get :index, :project_id => 1
120 120 assert_response :success
121 121 assert_template 'index.rhtml'
122 122 assert_not_nil assigns(:issues)
123 123 assert_tag :tag => 'a', :content => /Can't print recipes/
124 124 assert_tag :tag => 'a', :content => /Subproject issue/
125 125 assert_tag :tag => 'a', :content => /Issue of a private subproject/
126 126 end
127 127
128 128 def test_index_with_project_and_filter
129 129 get :index, :project_id => 1, :set_filter => 1
130 130 assert_response :success
131 131 assert_template 'index.rhtml'
132 132 assert_not_nil assigns(:issues)
133 133 end
134 134
135 135 def test_index_with_query
136 136 get :index, :project_id => 1, :query_id => 5
137 137 assert_response :success
138 138 assert_template 'index.rhtml'
139 139 assert_not_nil assigns(:issues)
140 140 assert_nil assigns(:issue_count_by_group)
141 141 end
142 142
143 143 def test_index_with_query_grouped_by_tracker
144 144 get :index, :project_id => 1, :query_id => 6
145 145 assert_response :success
146 146 assert_template 'index.rhtml'
147 147 assert_not_nil assigns(:issues)
148 148 assert_not_nil assigns(:issue_count_by_group)
149 149 end
150 150
151 151 def test_index_with_query_grouped_by_list_custom_field
152 152 get :index, :project_id => 1, :query_id => 9
153 153 assert_response :success
154 154 assert_template 'index.rhtml'
155 155 assert_not_nil assigns(:issues)
156 156 assert_not_nil assigns(:issue_count_by_group)
157 157 end
158 158
159 159 def test_index_sort_by_field_not_included_in_columns
160 160 Setting.issue_list_default_columns = %w(subject author)
161 161 get :index, :sort => 'tracker'
162 162 end
163 163
164 164 def test_index_csv_with_project
165 165 Setting.default_language = 'en'
166 166
167 167 get :index, :format => 'csv'
168 168 assert_response :success
169 169 assert_not_nil assigns(:issues)
170 170 assert_equal 'text/csv', @response.content_type
171 171 assert @response.body.starts_with?("#,")
172 172
173 173 get :index, :project_id => 1, :format => 'csv'
174 174 assert_response :success
175 175 assert_not_nil assigns(:issues)
176 176 assert_equal 'text/csv', @response.content_type
177 177 end
178 178
179 179 def test_index_pdf
180 180 get :index, :format => 'pdf'
181 181 assert_response :success
182 182 assert_not_nil assigns(:issues)
183 183 assert_equal 'application/pdf', @response.content_type
184 184
185 185 get :index, :project_id => 1, :format => 'pdf'
186 186 assert_response :success
187 187 assert_not_nil assigns(:issues)
188 188 assert_equal 'application/pdf', @response.content_type
189 189
190 190 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
191 191 assert_response :success
192 192 assert_not_nil assigns(:issues)
193 193 assert_equal 'application/pdf', @response.content_type
194 194 end
195 195
196 196 def test_index_pdf_with_query_grouped_by_list_custom_field
197 197 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
198 198 assert_response :success
199 199 assert_not_nil assigns(:issues)
200 200 assert_not_nil assigns(:issue_count_by_group)
201 201 assert_equal 'application/pdf', @response.content_type
202 202 end
203 203
204 204 def test_index_sort
205 205 get :index, :sort => 'tracker,id:desc'
206 206 assert_response :success
207 207
208 208 sort_params = @request.session['issues_index_sort']
209 209 assert sort_params.is_a?(String)
210 210 assert_equal 'tracker,id:desc', sort_params
211 211
212 212 issues = assigns(:issues)
213 213 assert_not_nil issues
214 214 assert !issues.empty?
215 215 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
216 216 end
217 217
218 218 def test_index_with_columns
219 219 columns = ['tracker', 'subject', 'assigned_to']
220 220 get :index, :set_filter => 1, :query => { 'column_names' => columns}
221 221 assert_response :success
222 222
223 223 # query should use specified columns
224 224 query = assigns(:query)
225 225 assert_kind_of Query, query
226 226 assert_equal columns, query.column_names.map(&:to_s)
227 227
228 228 # columns should be stored in session
229 229 assert_kind_of Hash, session[:query]
230 230 assert_kind_of Array, session[:query][:column_names]
231 231 assert_equal columns, session[:query][:column_names].map(&:to_s)
232 232 end
233 233
234 234 def test_gantt
235 235 get :gantt, :project_id => 1
236 236 assert_response :success
237 237 assert_template 'gantt.rhtml'
238 238 assert_not_nil assigns(:gantt)
239 239 events = assigns(:gantt).events
240 240 assert_not_nil events
241 241 # Issue with start and due dates
242 242 i = Issue.find(1)
243 243 assert_not_nil i.due_date
244 244 assert events.include?(Issue.find(1))
245 245 # Issue with without due date but targeted to a version with date
246 246 i = Issue.find(2)
247 247 assert_nil i.due_date
248 248 assert events.include?(i)
249 249 end
250 250
251 251 def test_cross_project_gantt
252 252 get :gantt
253 253 assert_response :success
254 254 assert_template 'gantt.rhtml'
255 255 assert_not_nil assigns(:gantt)
256 256 events = assigns(:gantt).events
257 257 assert_not_nil events
258 258 end
259 259
260 260 def test_gantt_export_to_pdf
261 261 get :gantt, :project_id => 1, :format => 'pdf'
262 262 assert_response :success
263 263 assert_equal 'application/pdf', @response.content_type
264 264 assert @response.body.starts_with?('%PDF')
265 265 assert_not_nil assigns(:gantt)
266 266 end
267 267
268 268 def test_cross_project_gantt_export_to_pdf
269 269 get :gantt, :format => 'pdf'
270 270 assert_response :success
271 271 assert_equal 'application/pdf', @response.content_type
272 272 assert @response.body.starts_with?('%PDF')
273 273 assert_not_nil assigns(:gantt)
274 274 end
275 275
276 276 if Object.const_defined?(:Magick)
277 277 def test_gantt_image
278 278 get :gantt, :project_id => 1, :format => 'png'
279 279 assert_response :success
280 280 assert_equal 'image/png', @response.content_type
281 281 end
282 282 else
283 283 puts "RMagick not installed. Skipping tests !!!"
284 284 end
285 285
286 286 def test_calendar
287 287 get :calendar, :project_id => 1
288 288 assert_response :success
289 289 assert_template 'calendar'
290 290 assert_not_nil assigns(:calendar)
291 291 end
292 292
293 293 def test_cross_project_calendar
294 294 get :calendar
295 295 assert_response :success
296 296 assert_template 'calendar'
297 297 assert_not_nil assigns(:calendar)
298 298 end
299 299
300 300 def test_changes
301 301 get :changes, :project_id => 1
302 302 assert_response :success
303 303 assert_not_nil assigns(:journals)
304 304 assert_equal 'application/atom+xml', @response.content_type
305 305 end
306 306
307 307 def test_show_by_anonymous
308 308 get :show, :id => 1
309 309 assert_response :success
310 310 assert_template 'show.rhtml'
311 311 assert_not_nil assigns(:issue)
312 312 assert_equal Issue.find(1), assigns(:issue)
313 313
314 314 # anonymous role is allowed to add a note
315 315 assert_tag :tag => 'form',
316 316 :descendant => { :tag => 'fieldset',
317 317 :child => { :tag => 'legend',
318 318 :content => /Notes/ } }
319 319 end
320 320
321 321 def test_show_by_manager
322 322 @request.session[:user_id] = 2
323 323 get :show, :id => 1
324 324 assert_response :success
325 325
326 326 assert_tag :tag => 'form',
327 327 :descendant => { :tag => 'fieldset',
328 328 :child => { :tag => 'legend',
329 329 :content => /Change properties/ } },
330 330 :descendant => { :tag => 'fieldset',
331 331 :child => { :tag => 'legend',
332 332 :content => /Log time/ } },
333 333 :descendant => { :tag => 'fieldset',
334 334 :child => { :tag => 'legend',
335 335 :content => /Notes/ } }
336 336 end
337 337
338 338 def test_show_should_deny_anonymous_access_without_permission
339 339 Role.anonymous.remove_permission!(:view_issues)
340 340 get :show, :id => 1
341 341 assert_response :redirect
342 342 end
343 343
344 344 def test_show_should_deny_non_member_access_without_permission
345 345 Role.non_member.remove_permission!(:view_issues)
346 346 @request.session[:user_id] = 9
347 347 get :show, :id => 1
348 348 assert_response 403
349 349 end
350 350
351 351 def test_show_should_deny_member_access_without_permission
352 352 Role.find(1).remove_permission!(:view_issues)
353 353 @request.session[:user_id] = 2
354 354 get :show, :id => 1
355 355 assert_response 403
356 356 end
357 357
358 358 def test_show_should_not_disclose_relations_to_invisible_issues
359 359 Setting.cross_project_issue_relations = '1'
360 360 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
361 361 # Relation to a private project issue
362 362 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
363 363
364 364 get :show, :id => 1
365 365 assert_response :success
366 366
367 367 assert_tag :div, :attributes => { :id => 'relations' },
368 368 :descendant => { :tag => 'a', :content => /#2$/ }
369 369 assert_no_tag :div, :attributes => { :id => 'relations' },
370 370 :descendant => { :tag => 'a', :content => /#4$/ }
371 371 end
372 372
373 373 def test_show_atom
374 374 get :show, :id => 2, :format => 'atom'
375 375 assert_response :success
376 376 assert_template 'changes.rxml'
377 377 # Inline image
378 378 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
379 379 end
380 380
381 381 def test_show_export_to_pdf
382 382 get :show, :id => 3, :format => 'pdf'
383 383 assert_response :success
384 384 assert_equal 'application/pdf', @response.content_type
385 385 assert @response.body.starts_with?('%PDF')
386 386 assert_not_nil assigns(:issue)
387 387 end
388 388
389 389 def test_get_new
390 390 @request.session[:user_id] = 2
391 391 get :new, :project_id => 1, :tracker_id => 1
392 392 assert_response :success
393 393 assert_template 'new'
394 394
395 395 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
396 396 :value => 'Default string' }
397 397 end
398 398
399 399 def test_get_new_without_tracker_id
400 400 @request.session[:user_id] = 2
401 401 get :new, :project_id => 1
402 402 assert_response :success
403 403 assert_template 'new'
404 404
405 405 issue = assigns(:issue)
406 406 assert_not_nil issue
407 407 assert_equal Project.find(1).trackers.first, issue.tracker
408 408 end
409 409
410 410 def test_get_new_with_no_default_status_should_display_an_error
411 411 @request.session[:user_id] = 2
412 412 IssueStatus.delete_all
413 413
414 414 get :new, :project_id => 1
415 415 assert_response 500
416 416 assert_not_nil flash[:error]
417 417 assert_tag :tag => 'div', :attributes => { :class => /error/ },
418 418 :content => /No default issue/
419 419 end
420 420
421 421 def test_get_new_with_no_tracker_should_display_an_error
422 422 @request.session[:user_id] = 2
423 423 Tracker.delete_all
424 424
425 425 get :new, :project_id => 1
426 426 assert_response 500
427 427 assert_not_nil flash[:error]
428 428 assert_tag :tag => 'div', :attributes => { :class => /error/ },
429 429 :content => /No tracker/
430 430 end
431 431
432 432 def test_update_new_form
433 433 @request.session[:user_id] = 2
434 434 xhr :post, :update_form, :project_id => 1,
435 435 :issue => {:tracker_id => 2,
436 436 :subject => 'This is the test_new issue',
437 437 :description => 'This is the description',
438 438 :priority_id => 5}
439 439 assert_response :success
440 440 assert_template 'attributes'
441 441
442 442 issue = assigns(:issue)
443 443 assert_kind_of Issue, issue
444 444 assert_equal 1, issue.project_id
445 445 assert_equal 2, issue.tracker_id
446 446 assert_equal 'This is the test_new issue', issue.subject
447 447 end
448 448
449 def test_post_new
449 def test_post_create
450 450 @request.session[:user_id] = 2
451 451 assert_difference 'Issue.count' do
452 post :new, :project_id => 1,
452 post :create, :project_id => 1,
453 453 :issue => {:tracker_id => 3,
454 454 :status_id => 2,
455 455 :subject => 'This is the test_new issue',
456 456 :description => 'This is the description',
457 457 :priority_id => 5,
458 458 :estimated_hours => '',
459 459 :custom_field_values => {'2' => 'Value for field 2'}}
460 460 end
461 461 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
462 462
463 463 issue = Issue.find_by_subject('This is the test_new issue')
464 464 assert_not_nil issue
465 465 assert_equal 2, issue.author_id
466 466 assert_equal 3, issue.tracker_id
467 467 assert_equal 2, issue.status_id
468 468 assert_nil issue.estimated_hours
469 469 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
470 470 assert_not_nil v
471 471 assert_equal 'Value for field 2', v.value
472 472 end
473 473
474 def test_post_new_and_continue
474 def test_post_create_and_continue
475 475 @request.session[:user_id] = 2
476 post :new, :project_id => 1,
476 post :create, :project_id => 1,
477 477 :issue => {:tracker_id => 3,
478 478 :subject => 'This is first issue',
479 479 :priority_id => 5},
480 480 :continue => ''
481 481 assert_redirected_to :controller => 'issues', :action => 'new', :issue => {:tracker_id => 3}
482 482 end
483 483
484 def test_post_new_without_custom_fields_param
484 def test_post_create_without_custom_fields_param
485 485 @request.session[:user_id] = 2
486 486 assert_difference 'Issue.count' do
487 post :new, :project_id => 1,
487 post :create, :project_id => 1,
488 488 :issue => {:tracker_id => 1,
489 489 :subject => 'This is the test_new issue',
490 490 :description => 'This is the description',
491 491 :priority_id => 5}
492 492 end
493 493 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
494 494 end
495 495
496 def test_post_new_with_required_custom_field_and_without_custom_fields_param
496 def test_post_create_with_required_custom_field_and_without_custom_fields_param
497 497 field = IssueCustomField.find_by_name('Database')
498 498 field.update_attribute(:is_required, true)
499 499
500 500 @request.session[:user_id] = 2
501 post :new, :project_id => 1,
501 post :create, :project_id => 1,
502 502 :issue => {:tracker_id => 1,
503 503 :subject => 'This is the test_new issue',
504 504 :description => 'This is the description',
505 505 :priority_id => 5}
506 506 assert_response :success
507 507 assert_template 'new'
508 508 issue = assigns(:issue)
509 509 assert_not_nil issue
510 510 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
511 511 end
512 512
513 def test_post_new_with_watchers
513 def test_post_create_with_watchers
514 514 @request.session[:user_id] = 2
515 515 ActionMailer::Base.deliveries.clear
516 516
517 517 assert_difference 'Watcher.count', 2 do
518 post :new, :project_id => 1,
518 post :create, :project_id => 1,
519 519 :issue => {:tracker_id => 1,
520 520 :subject => 'This is a new issue with watchers',
521 521 :description => 'This is the description',
522 522 :priority_id => 5,
523 523 :watcher_user_ids => ['2', '3']}
524 524 end
525 525 issue = Issue.find_by_subject('This is a new issue with watchers')
526 526 assert_not_nil issue
527 527 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
528 528
529 529 # Watchers added
530 530 assert_equal [2, 3], issue.watcher_user_ids.sort
531 531 assert issue.watched_by?(User.find(3))
532 532 # Watchers notified
533 533 mail = ActionMailer::Base.deliveries.last
534 534 assert_kind_of TMail::Mail, mail
535 535 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
536 536 end
537 537
538 def test_post_new_subissue
538 def test_post_create_subissue
539 539 @request.session[:user_id] = 2
540 540
541 541 assert_difference 'Issue.count' do
542 post :new, :project_id => 1,
542 post :create, :project_id => 1,
543 543 :issue => {:tracker_id => 1,
544 544 :subject => 'This is a child issue',
545 545 :parent_issue_id => 2}
546 546 end
547 547 issue = Issue.find_by_subject('This is a child issue')
548 548 assert_not_nil issue
549 549 assert_equal Issue.find(2), issue.parent
550 550 end
551 551
552 def test_post_new_should_send_a_notification
552 def test_post_create_should_send_a_notification
553 553 ActionMailer::Base.deliveries.clear
554 554 @request.session[:user_id] = 2
555 555 assert_difference 'Issue.count' do
556 post :new, :project_id => 1,
556 post :create, :project_id => 1,
557 557 :issue => {:tracker_id => 3,
558 558 :subject => 'This is the test_new issue',
559 559 :description => 'This is the description',
560 560 :priority_id => 5,
561 561 :estimated_hours => '',
562 562 :custom_field_values => {'2' => 'Value for field 2'}}
563 563 end
564 564 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
565 565
566 566 assert_equal 1, ActionMailer::Base.deliveries.size
567 567 end
568 568
569 def test_post_should_preserve_fields_values_on_validation_failure
569 def test_post_create_should_preserve_fields_values_on_validation_failure
570 570 @request.session[:user_id] = 2
571 post :new, :project_id => 1,
571 post :create, :project_id => 1,
572 572 :issue => {:tracker_id => 1,
573 573 # empty subject
574 574 :subject => '',
575 575 :description => 'This is a description',
576 576 :priority_id => 6,
577 577 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
578 578 assert_response :success
579 579 assert_template 'new'
580 580
581 581 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
582 582 :content => 'This is a description'
583 583 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
584 584 :child => { :tag => 'option', :attributes => { :selected => 'selected',
585 585 :value => '6' },
586 586 :content => 'High' }
587 587 # Custom fields
588 588 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
589 589 :child => { :tag => 'option', :attributes => { :selected => 'selected',
590 590 :value => 'Oracle' },
591 591 :content => 'Oracle' }
592 592 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
593 593 :value => 'Value for field 2'}
594 594 end
595 595
596 def test_post_new_should_ignore_non_safe_attributes
596 def test_post_create_should_ignore_non_safe_attributes
597 597 @request.session[:user_id] = 2
598 598 assert_nothing_raised do
599 post :new, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
599 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
600 600 end
601 601 end
602 602
603 603 context "without workflow privilege" do
604 604 setup do
605 605 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
606 606 Role.anonymous.add_permission! :add_issues
607 607 end
608 608
609 609 context "#new" do
610 610 should "propose default status only" do
611 611 get :new, :project_id => 1
612 612 assert_response :success
613 613 assert_template 'new'
614 614 assert_tag :tag => 'select',
615 615 :attributes => {:name => 'issue[status_id]'},
616 616 :children => {:count => 1},
617 617 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
618 618 end
619 619
620 620 should "accept default status" do
621 621 assert_difference 'Issue.count' do
622 post :new, :project_id => 1,
622 post :create, :project_id => 1,
623 623 :issue => {:tracker_id => 1,
624 624 :subject => 'This is an issue',
625 625 :status_id => 1}
626 626 end
627 627 issue = Issue.last(:order => 'id')
628 628 assert_equal IssueStatus.default, issue.status
629 629 end
630 630
631 631 should "ignore unauthorized status" do
632 632 assert_difference 'Issue.count' do
633 post :new, :project_id => 1,
633 post :create, :project_id => 1,
634 634 :issue => {:tracker_id => 1,
635 635 :subject => 'This is an issue',
636 636 :status_id => 3}
637 637 end
638 638 issue = Issue.last(:order => 'id')
639 639 assert_equal IssueStatus.default, issue.status
640 640 end
641 641 end
642 642 end
643 643
644 644 def test_copy_issue
645 645 @request.session[:user_id] = 2
646 646 get :new, :project_id => 1, :copy_from => 1
647 647 assert_template 'new'
648 648 assert_not_nil assigns(:issue)
649 649 orig = Issue.find(1)
650 650 assert_equal orig.subject, assigns(:issue).subject
651 651 end
652 652
653 653 def test_get_edit
654 654 @request.session[:user_id] = 2
655 655 get :edit, :id => 1
656 656 assert_response :success
657 657 assert_template 'edit'
658 658 assert_not_nil assigns(:issue)
659 659 assert_equal Issue.find(1), assigns(:issue)
660 660 end
661 661
662 662 def test_get_edit_with_params
663 663 @request.session[:user_id] = 2
664 664 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
665 665 assert_response :success
666 666 assert_template 'edit'
667 667
668 668 issue = assigns(:issue)
669 669 assert_not_nil issue
670 670
671 671 assert_equal 5, issue.status_id
672 672 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
673 673 :child => { :tag => 'option',
674 674 :content => 'Closed',
675 675 :attributes => { :selected => 'selected' } }
676 676
677 677 assert_equal 7, issue.priority_id
678 678 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
679 679 :child => { :tag => 'option',
680 680 :content => 'Urgent',
681 681 :attributes => { :selected => 'selected' } }
682 682 end
683 683
684 684 def test_update_edit_form
685 685 @request.session[:user_id] = 2
686 686 xhr :post, :update_form, :project_id => 1,
687 687 :id => 1,
688 688 :issue => {:tracker_id => 2,
689 689 :subject => 'This is the test_new issue',
690 690 :description => 'This is the description',
691 691 :priority_id => 5}
692 692 assert_response :success
693 693 assert_template 'attributes'
694 694
695 695 issue = assigns(:issue)
696 696 assert_kind_of Issue, issue
697 697 assert_equal 1, issue.id
698 698 assert_equal 1, issue.project_id
699 699 assert_equal 2, issue.tracker_id
700 700 assert_equal 'This is the test_new issue', issue.subject
701 701 end
702 702
703 703 def test_reply_to_issue
704 704 @request.session[:user_id] = 2
705 705 get :reply, :id => 1
706 706 assert_response :success
707 707 assert_select_rjs :show, "update"
708 708 end
709 709
710 710 def test_reply_to_note
711 711 @request.session[:user_id] = 2
712 712 get :reply, :id => 1, :journal_id => 2
713 713 assert_response :success
714 714 assert_select_rjs :show, "update"
715 715 end
716 716
717 717 def test_update_using_invalid_http_verbs
718 718 @request.session[:user_id] = 2
719 719 subject = 'Updated by an invalid http verb'
720 720
721 721 get :update, :id => 1, :issue => {:subject => subject}
722 722 assert_not_equal subject, Issue.find(1).subject
723 723
724 724 post :update, :id => 1, :issue => {:subject => subject}
725 725 assert_not_equal subject, Issue.find(1).subject
726 726
727 727 delete :update, :id => 1, :issue => {:subject => subject}
728 728 assert_not_equal subject, Issue.find(1).subject
729 729 end
730 730
731 731 def test_put_update_without_custom_fields_param
732 732 @request.session[:user_id] = 2
733 733 ActionMailer::Base.deliveries.clear
734 734
735 735 issue = Issue.find(1)
736 736 assert_equal '125', issue.custom_value_for(2).value
737 737 old_subject = issue.subject
738 738 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
739 739
740 740 assert_difference('Journal.count') do
741 741 assert_difference('JournalDetail.count', 2) do
742 742 put :update, :id => 1, :issue => {:subject => new_subject,
743 743 :priority_id => '6',
744 744 :category_id => '1' # no change
745 745 }
746 746 end
747 747 end
748 748 assert_redirected_to :action => 'show', :id => '1'
749 749 issue.reload
750 750 assert_equal new_subject, issue.subject
751 751 # Make sure custom fields were not cleared
752 752 assert_equal '125', issue.custom_value_for(2).value
753 753
754 754 mail = ActionMailer::Base.deliveries.last
755 755 assert_kind_of TMail::Mail, mail
756 756 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
757 757 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
758 758 end
759 759
760 760 def test_put_update_with_custom_field_change
761 761 @request.session[:user_id] = 2
762 762 issue = Issue.find(1)
763 763 assert_equal '125', issue.custom_value_for(2).value
764 764
765 765 assert_difference('Journal.count') do
766 766 assert_difference('JournalDetail.count', 3) do
767 767 put :update, :id => 1, :issue => {:subject => 'Custom field change',
768 768 :priority_id => '6',
769 769 :category_id => '1', # no change
770 770 :custom_field_values => { '2' => 'New custom value' }
771 771 }
772 772 end
773 773 end
774 774 assert_redirected_to :action => 'show', :id => '1'
775 775 issue.reload
776 776 assert_equal 'New custom value', issue.custom_value_for(2).value
777 777
778 778 mail = ActionMailer::Base.deliveries.last
779 779 assert_kind_of TMail::Mail, mail
780 780 assert mail.body.include?("Searchable field changed from 125 to New custom value")
781 781 end
782 782
783 783 def test_put_update_with_status_and_assignee_change
784 784 issue = Issue.find(1)
785 785 assert_equal 1, issue.status_id
786 786 @request.session[:user_id] = 2
787 787 assert_difference('TimeEntry.count', 0) do
788 788 put :update,
789 789 :id => 1,
790 790 :issue => { :status_id => 2, :assigned_to_id => 3 },
791 791 :notes => 'Assigned to dlopper',
792 792 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
793 793 end
794 794 assert_redirected_to :action => 'show', :id => '1'
795 795 issue.reload
796 796 assert_equal 2, issue.status_id
797 797 j = Journal.find(:first, :order => 'id DESC')
798 798 assert_equal 'Assigned to dlopper', j.notes
799 799 assert_equal 2, j.details.size
800 800
801 801 mail = ActionMailer::Base.deliveries.last
802 802 assert mail.body.include?("Status changed from New to Assigned")
803 803 # subject should contain the new status
804 804 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
805 805 end
806 806
807 807 def test_put_update_with_note_only
808 808 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
809 809 # anonymous user
810 810 put :update,
811 811 :id => 1,
812 812 :notes => notes
813 813 assert_redirected_to :action => 'show', :id => '1'
814 814 j = Journal.find(:first, :order => 'id DESC')
815 815 assert_equal notes, j.notes
816 816 assert_equal 0, j.details.size
817 817 assert_equal User.anonymous, j.user
818 818
819 819 mail = ActionMailer::Base.deliveries.last
820 820 assert mail.body.include?(notes)
821 821 end
822 822
823 823 def test_put_update_with_note_and_spent_time
824 824 @request.session[:user_id] = 2
825 825 spent_hours_before = Issue.find(1).spent_hours
826 826 assert_difference('TimeEntry.count') do
827 827 put :update,
828 828 :id => 1,
829 829 :notes => '2.5 hours added',
830 830 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first }
831 831 end
832 832 assert_redirected_to :action => 'show', :id => '1'
833 833
834 834 issue = Issue.find(1)
835 835
836 836 j = Journal.find(:first, :order => 'id DESC')
837 837 assert_equal '2.5 hours added', j.notes
838 838 assert_equal 0, j.details.size
839 839
840 840 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
841 841 assert_not_nil t
842 842 assert_equal 2.5, t.hours
843 843 assert_equal spent_hours_before + 2.5, issue.spent_hours
844 844 end
845 845
846 846 def test_put_update_with_attachment_only
847 847 set_tmp_attachments_directory
848 848
849 849 # Delete all fixtured journals, a race condition can occur causing the wrong
850 850 # journal to get fetched in the next find.
851 851 Journal.delete_all
852 852
853 853 # anonymous user
854 854 put :update,
855 855 :id => 1,
856 856 :notes => '',
857 857 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
858 858 assert_redirected_to :action => 'show', :id => '1'
859 859 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
860 860 assert j.notes.blank?
861 861 assert_equal 1, j.details.size
862 862 assert_equal 'testfile.txt', j.details.first.value
863 863 assert_equal User.anonymous, j.user
864 864
865 865 mail = ActionMailer::Base.deliveries.last
866 866 assert mail.body.include?('testfile.txt')
867 867 end
868 868
869 869 def test_put_update_with_attachment_that_fails_to_save
870 870 set_tmp_attachments_directory
871 871
872 872 # Delete all fixtured journals, a race condition can occur causing the wrong
873 873 # journal to get fetched in the next find.
874 874 Journal.delete_all
875 875
876 876 # Mock out the unsaved attachment
877 877 Attachment.any_instance.stubs(:create).returns(Attachment.new)
878 878
879 879 # anonymous user
880 880 put :update,
881 881 :id => 1,
882 882 :notes => '',
883 883 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
884 884 assert_redirected_to :action => 'show', :id => '1'
885 885 assert_equal '1 file(s) could not be saved.', flash[:warning]
886 886
887 887 end if Object.const_defined?(:Mocha)
888 888
889 889 def test_put_update_with_no_change
890 890 issue = Issue.find(1)
891 891 issue.journals.clear
892 892 ActionMailer::Base.deliveries.clear
893 893
894 894 put :update,
895 895 :id => 1,
896 896 :notes => ''
897 897 assert_redirected_to :action => 'show', :id => '1'
898 898
899 899 issue.reload
900 900 assert issue.journals.empty?
901 901 # No email should be sent
902 902 assert ActionMailer::Base.deliveries.empty?
903 903 end
904 904
905 905 def test_put_update_should_send_a_notification
906 906 @request.session[:user_id] = 2
907 907 ActionMailer::Base.deliveries.clear
908 908 issue = Issue.find(1)
909 909 old_subject = issue.subject
910 910 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
911 911
912 912 put :update, :id => 1, :issue => {:subject => new_subject,
913 913 :priority_id => '6',
914 914 :category_id => '1' # no change
915 915 }
916 916 assert_equal 1, ActionMailer::Base.deliveries.size
917 917 end
918 918
919 919 def test_put_update_with_invalid_spent_time
920 920 @request.session[:user_id] = 2
921 921 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
922 922
923 923 assert_no_difference('Journal.count') do
924 924 put :update,
925 925 :id => 1,
926 926 :notes => notes,
927 927 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
928 928 end
929 929 assert_response :success
930 930 assert_template 'edit'
931 931
932 932 assert_tag :textarea, :attributes => { :name => 'notes' },
933 933 :content => notes
934 934 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
935 935 end
936 936
937 937 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
938 938 issue = Issue.find(2)
939 939 @request.session[:user_id] = 2
940 940
941 941 put :update,
942 942 :id => issue.id,
943 943 :issue => {
944 944 :fixed_version_id => 4
945 945 }
946 946
947 947 assert_response :redirect
948 948 issue.reload
949 949 assert_equal 4, issue.fixed_version_id
950 950 assert_not_equal issue.project_id, issue.fixed_version.project_id
951 951 end
952 952
953 953 def test_put_update_should_redirect_back_using_the_back_url_parameter
954 954 issue = Issue.find(2)
955 955 @request.session[:user_id] = 2
956 956
957 957 put :update,
958 958 :id => issue.id,
959 959 :issue => {
960 960 :fixed_version_id => 4
961 961 },
962 962 :back_url => '/issues'
963 963
964 964 assert_response :redirect
965 965 assert_redirected_to '/issues'
966 966 end
967 967
968 968 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
969 969 issue = Issue.find(2)
970 970 @request.session[:user_id] = 2
971 971
972 972 put :update,
973 973 :id => issue.id,
974 974 :issue => {
975 975 :fixed_version_id => 4
976 976 },
977 977 :back_url => 'http://google.com'
978 978
979 979 assert_response :redirect
980 980 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
981 981 end
982 982
983 983 def test_get_bulk_edit
984 984 @request.session[:user_id] = 2
985 985 get :bulk_edit, :ids => [1, 2]
986 986 assert_response :success
987 987 assert_template 'bulk_edit'
988 988
989 989 # Project specific custom field, date type
990 990 field = CustomField.find(9)
991 991 assert !field.is_for_all?
992 992 assert_equal 'date', field.field_format
993 993 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
994 994
995 995 # System wide custom field
996 996 assert CustomField.find(1).is_for_all?
997 997 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
998 998 end
999 999
1000 1000 def test_bulk_edit
1001 1001 @request.session[:user_id] = 2
1002 1002 # update issues priority
1003 1003 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing',
1004 1004 :issue => {:priority_id => 7,
1005 1005 :assigned_to_id => '',
1006 1006 :custom_field_values => {'2' => ''}}
1007 1007
1008 1008 assert_response 302
1009 1009 # check that the issues were updated
1010 1010 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
1011 1011
1012 1012 issue = Issue.find(1)
1013 1013 journal = issue.journals.find(:first, :order => 'created_on DESC')
1014 1014 assert_equal '125', issue.custom_value_for(2).value
1015 1015 assert_equal 'Bulk editing', journal.notes
1016 1016 assert_equal 1, journal.details.size
1017 1017 end
1018 1018
1019 1019 def test_bullk_edit_should_send_a_notification
1020 1020 @request.session[:user_id] = 2
1021 1021 ActionMailer::Base.deliveries.clear
1022 1022 post(:bulk_edit,
1023 1023 {
1024 1024 :ids => [1, 2],
1025 1025 :notes => 'Bulk editing',
1026 1026 :issue => {
1027 1027 :priority_id => 7,
1028 1028 :assigned_to_id => '',
1029 1029 :custom_field_values => {'2' => ''}
1030 1030 }
1031 1031 })
1032 1032
1033 1033 assert_response 302
1034 1034 assert_equal 2, ActionMailer::Base.deliveries.size
1035 1035 end
1036 1036
1037 1037 def test_bulk_edit_status
1038 1038 @request.session[:user_id] = 2
1039 1039 # update issues priority
1040 1040 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing status',
1041 1041 :issue => {:priority_id => '',
1042 1042 :assigned_to_id => '',
1043 1043 :status_id => '5'}
1044 1044
1045 1045 assert_response 302
1046 1046 issue = Issue.find(1)
1047 1047 assert issue.closed?
1048 1048 end
1049 1049
1050 1050 def test_bulk_edit_custom_field
1051 1051 @request.session[:user_id] = 2
1052 1052 # update issues priority
1053 1053 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk editing custom field',
1054 1054 :issue => {:priority_id => '',
1055 1055 :assigned_to_id => '',
1056 1056 :custom_field_values => {'2' => '777'}}
1057 1057
1058 1058 assert_response 302
1059 1059
1060 1060 issue = Issue.find(1)
1061 1061 journal = issue.journals.find(:first, :order => 'created_on DESC')
1062 1062 assert_equal '777', issue.custom_value_for(2).value
1063 1063 assert_equal 1, journal.details.size
1064 1064 assert_equal '125', journal.details.first.old_value
1065 1065 assert_equal '777', journal.details.first.value
1066 1066 end
1067 1067
1068 1068 def test_bulk_unassign
1069 1069 assert_not_nil Issue.find(2).assigned_to
1070 1070 @request.session[:user_id] = 2
1071 1071 # unassign issues
1072 1072 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
1073 1073 assert_response 302
1074 1074 # check that the issues were updated
1075 1075 assert_nil Issue.find(2).assigned_to
1076 1076 end
1077 1077
1078 1078 def test_post_bulk_edit_should_allow_fixed_version_to_be_set_to_a_subproject
1079 1079 @request.session[:user_id] = 2
1080 1080
1081 1081 post :bulk_edit, :ids => [1,2], :issue => {:fixed_version_id => 4}
1082 1082
1083 1083 assert_response :redirect
1084 1084 issues = Issue.find([1,2])
1085 1085 issues.each do |issue|
1086 1086 assert_equal 4, issue.fixed_version_id
1087 1087 assert_not_equal issue.project_id, issue.fixed_version.project_id
1088 1088 end
1089 1089 end
1090 1090
1091 1091 def test_post_bulk_edit_should_redirect_back_using_the_back_url_parameter
1092 1092 @request.session[:user_id] = 2
1093 1093 post :bulk_edit, :ids => [1,2], :back_url => '/issues'
1094 1094
1095 1095 assert_response :redirect
1096 1096 assert_redirected_to '/issues'
1097 1097 end
1098 1098
1099 1099 def test_post_bulk_edit_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1100 1100 @request.session[:user_id] = 2
1101 1101 post :bulk_edit, :ids => [1,2], :back_url => 'http://google.com'
1102 1102
1103 1103 assert_response :redirect
1104 1104 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
1105 1105 end
1106 1106
1107 1107 def test_move_one_issue_to_another_project
1108 1108 @request.session[:user_id] = 2
1109 1109 post :move, :id => 1, :new_project_id => 2, :tracker_id => '', :assigned_to_id => '', :status_id => '', :start_date => '', :due_date => ''
1110 1110 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1111 1111 assert_equal 2, Issue.find(1).project_id
1112 1112 end
1113 1113
1114 1114 def test_move_one_issue_to_another_project_should_follow_when_needed
1115 1115 @request.session[:user_id] = 2
1116 1116 post :move, :id => 1, :new_project_id => 2, :follow => '1'
1117 1117 assert_redirected_to '/issues/1'
1118 1118 end
1119 1119
1120 1120 def test_bulk_move_to_another_project
1121 1121 @request.session[:user_id] = 2
1122 1122 post :move, :ids => [1, 2], :new_project_id => 2
1123 1123 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1124 1124 # Issues moved to project 2
1125 1125 assert_equal 2, Issue.find(1).project_id
1126 1126 assert_equal 2, Issue.find(2).project_id
1127 1127 # No tracker change
1128 1128 assert_equal 1, Issue.find(1).tracker_id
1129 1129 assert_equal 2, Issue.find(2).tracker_id
1130 1130 end
1131 1131
1132 1132 def test_bulk_move_to_another_tracker
1133 1133 @request.session[:user_id] = 2
1134 1134 post :move, :ids => [1, 2], :new_tracker_id => 2
1135 1135 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1136 1136 assert_equal 2, Issue.find(1).tracker_id
1137 1137 assert_equal 2, Issue.find(2).tracker_id
1138 1138 end
1139 1139
1140 1140 def test_bulk_copy_to_another_project
1141 1141 @request.session[:user_id] = 2
1142 1142 assert_difference 'Issue.count', 2 do
1143 1143 assert_no_difference 'Project.find(1).issues.count' do
1144 1144 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}
1145 1145 end
1146 1146 end
1147 1147 assert_redirected_to 'projects/ecookbook/issues'
1148 1148 end
1149 1149
1150 1150 context "#move via bulk copy" do
1151 1151 should "allow not changing the issue's attributes" do
1152 1152 @request.session[:user_id] = 2
1153 1153 issue_before_move = Issue.find(1)
1154 1154 assert_difference 'Issue.count', 1 do
1155 1155 assert_no_difference 'Project.find(1).issues.count' do
1156 1156 post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :new_tracker_id => '', :assigned_to_id => '', :status_id => '', :start_date => '', :due_date => ''
1157 1157 end
1158 1158 end
1159 1159 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
1160 1160 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
1161 1161 assert_equal issue_before_move.status_id, issue_after_move.status_id
1162 1162 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
1163 1163 end
1164 1164
1165 1165 should "allow changing the issue's attributes" do
1166 1166 @request.session[:user_id] = 2
1167 1167 assert_difference 'Issue.count', 2 do
1168 1168 assert_no_difference 'Project.find(1).issues.count' do
1169 1169 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}, :new_tracker_id => '', :assigned_to_id => 4, :status_id => 3, :start_date => '2009-12-01', :due_date => '2009-12-31'
1170 1170 end
1171 1171 end
1172 1172
1173 1173 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
1174 1174 assert_equal 2, copied_issues.size
1175 1175 copied_issues.each do |issue|
1176 1176 assert_equal 2, issue.project_id, "Project is incorrect"
1177 1177 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
1178 1178 assert_equal 3, issue.status_id, "Status is incorrect"
1179 1179 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
1180 1180 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
1181 1181 end
1182 1182 end
1183 1183 end
1184 1184
1185 1185 def test_copy_to_another_project_should_follow_when_needed
1186 1186 @request.session[:user_id] = 2
1187 1187 post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :follow => '1'
1188 1188 issue = Issue.first(:order => 'id DESC')
1189 1189 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1190 1190 end
1191 1191
1192 1192 def test_context_menu_one_issue
1193 1193 @request.session[:user_id] = 2
1194 1194 get :context_menu, :ids => [1]
1195 1195 assert_response :success
1196 1196 assert_template 'context_menu'
1197 1197 assert_tag :tag => 'a', :content => 'Edit',
1198 1198 :attributes => { :href => '/issues/1/edit',
1199 1199 :class => 'icon-edit' }
1200 1200 assert_tag :tag => 'a', :content => 'Closed',
1201 1201 :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5',
1202 1202 :class => '' }
1203 1203 assert_tag :tag => 'a', :content => 'Immediate',
1204 1204 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bpriority_id%5D=8',
1205 1205 :class => '' }
1206 1206 # Versions
1207 1207 assert_tag :tag => 'a', :content => '2.0',
1208 1208 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=3',
1209 1209 :class => '' }
1210 1210 assert_tag :tag => 'a', :content => 'eCookbook Subproject 1 - 2.0',
1211 1211 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=4',
1212 1212 :class => '' }
1213 1213
1214 1214 assert_tag :tag => 'a', :content => 'Dave Lopper',
1215 1215 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bassigned_to_id%5D=3',
1216 1216 :class => '' }
1217 1217 assert_tag :tag => 'a', :content => 'Duplicate',
1218 1218 :attributes => { :href => '/projects/ecookbook/issues/1/copy',
1219 1219 :class => 'icon-duplicate' }
1220 1220 assert_tag :tag => 'a', :content => 'Copy',
1221 1221 :attributes => { :href => '/issues/move?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1',
1222 1222 :class => 'icon-copy' }
1223 1223 assert_tag :tag => 'a', :content => 'Move',
1224 1224 :attributes => { :href => '/issues/move?ids%5B%5D=1',
1225 1225 :class => 'icon-move' }
1226 1226 assert_tag :tag => 'a', :content => 'Delete',
1227 1227 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
1228 1228 :class => 'icon-del' }
1229 1229 end
1230 1230
1231 1231 def test_context_menu_one_issue_by_anonymous
1232 1232 get :context_menu, :ids => [1]
1233 1233 assert_response :success
1234 1234 assert_template 'context_menu'
1235 1235 assert_tag :tag => 'a', :content => 'Delete',
1236 1236 :attributes => { :href => '#',
1237 1237 :class => 'icon-del disabled' }
1238 1238 end
1239 1239
1240 1240 def test_context_menu_multiple_issues_of_same_project
1241 1241 @request.session[:user_id] = 2
1242 1242 get :context_menu, :ids => [1, 2]
1243 1243 assert_response :success
1244 1244 assert_template 'context_menu'
1245 1245 assert_tag :tag => 'a', :content => 'Edit',
1246 1246 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
1247 1247 :class => 'icon-edit' }
1248 1248 assert_tag :tag => 'a', :content => 'Immediate',
1249 1249 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;issue%5Bpriority_id%5D=8',
1250 1250 :class => '' }
1251 1251 assert_tag :tag => 'a', :content => 'Dave Lopper',
1252 1252 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;issue%5Bassigned_to_id%5D=3',
1253 1253 :class => '' }
1254 1254 assert_tag :tag => 'a', :content => 'Copy',
1255 1255 :attributes => { :href => '/issues/move?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
1256 1256 :class => 'icon-copy' }
1257 1257 assert_tag :tag => 'a', :content => 'Move',
1258 1258 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
1259 1259 :class => 'icon-move' }
1260 1260 assert_tag :tag => 'a', :content => 'Delete',
1261 1261 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
1262 1262 :class => 'icon-del' }
1263 1263 end
1264 1264
1265 1265 def test_context_menu_multiple_issues_of_different_project
1266 1266 @request.session[:user_id] = 2
1267 1267 get :context_menu, :ids => [1, 2, 4]
1268 1268 assert_response :success
1269 1269 assert_template 'context_menu'
1270 1270 assert_tag :tag => 'a', :content => 'Delete',
1271 1271 :attributes => { :href => '#',
1272 1272 :class => 'icon-del disabled' }
1273 1273 end
1274 1274
1275 1275 def test_preview_new_issue
1276 1276 @request.session[:user_id] = 2
1277 1277 post :preview, :project_id => '1', :issue => {:description => 'Foo'}
1278 1278 assert_response :success
1279 1279 assert_template 'preview'
1280 1280 assert_not_nil assigns(:description)
1281 1281 end
1282 1282
1283 1283 def test_preview_notes
1284 1284 @request.session[:user_id] = 2
1285 1285 post :preview, :project_id => '1', :id => 1, :issue => {:description => Issue.find(1).description}, :notes => 'Foo'
1286 1286 assert_response :success
1287 1287 assert_template 'preview'
1288 1288 assert_not_nil assigns(:notes)
1289 1289 end
1290 1290
1291 1291 def test_auto_complete_should_not_be_case_sensitive
1292 1292 get :auto_complete, :project_id => 'ecookbook', :q => 'ReCiPe'
1293 1293 assert_response :success
1294 1294 assert_not_nil assigns(:issues)
1295 1295 assert assigns(:issues).detect {|issue| issue.subject.match /recipe/}
1296 1296 end
1297 1297
1298 1298 def test_auto_complete_should_return_issue_with_given_id
1299 1299 get :auto_complete, :project_id => 'subproject1', :q => '13'
1300 1300 assert_response :success
1301 1301 assert_not_nil assigns(:issues)
1302 1302 assert assigns(:issues).include?(Issue.find(13))
1303 1303 end
1304 1304
1305 1305 def test_destroy_issue_with_no_time_entries
1306 1306 assert_nil TimeEntry.find_by_issue_id(2)
1307 1307 @request.session[:user_id] = 2
1308 1308 post :destroy, :id => 2
1309 1309 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1310 1310 assert_nil Issue.find_by_id(2)
1311 1311 end
1312 1312
1313 1313 def test_destroy_issues_with_time_entries
1314 1314 @request.session[:user_id] = 2
1315 1315 post :destroy, :ids => [1, 3]
1316 1316 assert_response :success
1317 1317 assert_template 'destroy'
1318 1318 assert_not_nil assigns(:hours)
1319 1319 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1320 1320 end
1321 1321
1322 1322 def test_destroy_issues_and_destroy_time_entries
1323 1323 @request.session[:user_id] = 2
1324 1324 post :destroy, :ids => [1, 3], :todo => 'destroy'
1325 1325 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1326 1326 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1327 1327 assert_nil TimeEntry.find_by_id([1, 2])
1328 1328 end
1329 1329
1330 1330 def test_destroy_issues_and_assign_time_entries_to_project
1331 1331 @request.session[:user_id] = 2
1332 1332 post :destroy, :ids => [1, 3], :todo => 'nullify'
1333 1333 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1334 1334 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1335 1335 assert_nil TimeEntry.find(1).issue_id
1336 1336 assert_nil TimeEntry.find(2).issue_id
1337 1337 end
1338 1338
1339 1339 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1340 1340 @request.session[:user_id] = 2
1341 1341 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1342 1342 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1343 1343 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1344 1344 assert_equal 2, TimeEntry.find(1).issue_id
1345 1345 assert_equal 2, TimeEntry.find(2).issue_id
1346 1346 end
1347 1347
1348 1348 def test_default_search_scope
1349 1349 get :index
1350 1350 assert_tag :div, :attributes => {:id => 'quick-search'},
1351 1351 :child => {:tag => 'form',
1352 1352 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1353 1353 end
1354 1354 end
@@ -1,280 +1,281
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2010 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require "#{File.dirname(__FILE__)}/../test_helper"
19 19
20 20 class RoutingTest < ActionController::IntegrationTest
21 21 context "activities" do
22 22 should_route :get, "/activity", :controller => 'projects', :action => 'activity', :id => nil
23 23 should_route :get, "/activity.atom", :controller => 'projects', :action => 'activity', :id => nil, :format => 'atom'
24 24 end
25 25
26 26 context "attachments" do
27 27 should_route :get, "/attachments/1", :controller => 'attachments', :action => 'show', :id => '1'
28 28 should_route :get, "/attachments/1/filename.ext", :controller => 'attachments', :action => 'show', :id => '1', :filename => 'filename.ext'
29 29 should_route :get, "/attachments/download/1", :controller => 'attachments', :action => 'download', :id => '1'
30 30 should_route :get, "/attachments/download/1/filename.ext", :controller => 'attachments', :action => 'download', :id => '1', :filename => 'filename.ext'
31 31 end
32 32
33 33 context "boards" do
34 34 should_route :get, "/projects/world_domination/boards", :controller => 'boards', :action => 'index', :project_id => 'world_domination'
35 35 should_route :get, "/projects/world_domination/boards/new", :controller => 'boards', :action => 'new', :project_id => 'world_domination'
36 36 should_route :get, "/projects/world_domination/boards/44", :controller => 'boards', :action => 'show', :project_id => 'world_domination', :id => '44'
37 37 should_route :get, "/projects/world_domination/boards/44.atom", :controller => 'boards', :action => 'show', :project_id => 'world_domination', :id => '44', :format => 'atom'
38 38 should_route :get, "/projects/world_domination/boards/44/edit", :controller => 'boards', :action => 'edit', :project_id => 'world_domination', :id => '44'
39 39
40 40 should_route :post, "/projects/world_domination/boards/new", :controller => 'boards', :action => 'new', :project_id => 'world_domination'
41 41 should_route :post, "/projects/world_domination/boards/44/edit", :controller => 'boards', :action => 'edit', :project_id => 'world_domination', :id => '44'
42 42 should_route :post, "/projects/world_domination/boards/44/destroy", :controller => 'boards', :action => 'destroy', :project_id => 'world_domination', :id => '44'
43 43
44 44 end
45 45
46 46 context "documents" do
47 47 should_route :get, "/projects/567/documents", :controller => 'documents', :action => 'index', :project_id => '567'
48 48 should_route :get, "/projects/567/documents/new", :controller => 'documents', :action => 'new', :project_id => '567'
49 49 should_route :get, "/documents/22", :controller => 'documents', :action => 'show', :id => '22'
50 50 should_route :get, "/documents/22/edit", :controller => 'documents', :action => 'edit', :id => '22'
51 51
52 52 should_route :post, "/projects/567/documents/new", :controller => 'documents', :action => 'new', :project_id => '567'
53 53 should_route :post, "/documents/567/edit", :controller => 'documents', :action => 'edit', :id => '567'
54 54 should_route :post, "/documents/567/destroy", :controller => 'documents', :action => 'destroy', :id => '567'
55 55 end
56 56
57 57 context "issues" do
58 58 # REST actions
59 59 should_route :get, "/issues", :controller => 'issues', :action => 'index'
60 60 should_route :get, "/issues.pdf", :controller => 'issues', :action => 'index', :format => 'pdf'
61 61 should_route :get, "/issues.atom", :controller => 'issues', :action => 'index', :format => 'atom'
62 62 should_route :get, "/issues.xml", :controller => 'issues', :action => 'index', :format => 'xml'
63 63 should_route :get, "/projects/23/issues", :controller => 'issues', :action => 'index', :project_id => '23'
64 64 should_route :get, "/projects/23/issues.pdf", :controller => 'issues', :action => 'index', :project_id => '23', :format => 'pdf'
65 65 should_route :get, "/projects/23/issues.atom", :controller => 'issues', :action => 'index', :project_id => '23', :format => 'atom'
66 66 should_route :get, "/projects/23/issues.xml", :controller => 'issues', :action => 'index', :project_id => '23', :format => 'xml'
67 67 should_route :get, "/issues/64", :controller => 'issues', :action => 'show', :id => '64'
68 68 should_route :get, "/issues/64.pdf", :controller => 'issues', :action => 'show', :id => '64', :format => 'pdf'
69 69 should_route :get, "/issues/64.atom", :controller => 'issues', :action => 'show', :id => '64', :format => 'atom'
70 70 should_route :get, "/issues/64.xml", :controller => 'issues', :action => 'show', :id => '64', :format => 'xml'
71 71
72 72 should_route :get, "/projects/23/issues/new", :controller => 'issues', :action => 'new', :project_id => '23'
73 should_route :post, "/issues.xml", :controller => 'issues', :action => 'new', :format => 'xml'
73 should_route :post, "/projects/23/issues", :controller => 'issues', :action => 'create', :project_id => '23'
74 should_route :post, "/issues.xml", :controller => 'issues', :action => 'create', :format => 'xml'
74 75
75 76 should_route :get, "/issues/64/edit", :controller => 'issues', :action => 'edit', :id => '64'
76 77 # TODO: Should use PUT
77 78 should_route :post, "/issues/64/edit", :controller => 'issues', :action => 'edit', :id => '64'
78 79 should_route :put, "/issues/1.xml", :controller => 'issues', :action => 'update', :id => '1', :format => 'xml'
79 80
80 81 # TODO: Should use DELETE
81 82 should_route :post, "/issues/64/destroy", :controller => 'issues', :action => 'destroy', :id => '64'
82 83 should_route :delete, "/issues/1.xml", :controller => 'issues', :action => 'destroy', :id => '1', :format => 'xml'
83 84
84 85 # Extra actions
85 86 should_route :get, "/projects/23/issues/64/copy", :controller => 'issues', :action => 'new', :project_id => '23', :copy_from => '64'
86 87
87 88 should_route :get, "/issues/1/move", :controller => 'issues', :action => 'move', :id => '1'
88 89 should_route :post, "/issues/1/move", :controller => 'issues', :action => 'move', :id => '1'
89 90
90 91 should_route :post, "/issues/1/quoted", :controller => 'issues', :action => 'reply', :id => '1'
91 92
92 93 should_route :get, "/issues/calendar", :controller => 'issues', :action => 'calendar'
93 94 should_route :post, "/issues/calendar", :controller => 'issues', :action => 'calendar'
94 95 should_route :get, "/projects/project-name/issues/calendar", :controller => 'issues', :action => 'calendar', :project_id => 'project-name'
95 96 should_route :post, "/projects/project-name/issues/calendar", :controller => 'issues', :action => 'calendar', :project_id => 'project-name'
96 97
97 98 should_route :get, "/issues/gantt", :controller => 'issues', :action => 'gantt'
98 99 should_route :post, "/issues/gantt", :controller => 'issues', :action => 'gantt'
99 100 should_route :get, "/projects/project-name/issues/gantt", :controller => 'issues', :action => 'gantt', :project_id => 'project-name'
100 101 should_route :post, "/projects/project-name/issues/gantt", :controller => 'issues', :action => 'gantt', :project_id => 'project-name'
101 102
102 103 should_route :get, "/issues/auto_complete", :controller => 'issues', :action => 'auto_complete'
103 104 end
104 105
105 106 context "issue categories" do
106 107 should_route :get, "/projects/test/issue_categories/new", :controller => 'issue_categories', :action => 'new', :project_id => 'test'
107 108
108 109 should_route :post, "/projects/test/issue_categories/new", :controller => 'issue_categories', :action => 'new', :project_id => 'test'
109 110 end
110 111
111 112 context "issue relations" do
112 113 should_route :post, "/issues/1/relations", :controller => 'issue_relations', :action => 'new', :issue_id => '1'
113 114 should_route :post, "/issues/1/relations/23/destroy", :controller => 'issue_relations', :action => 'destroy', :issue_id => '1', :id => '23'
114 115 end
115 116
116 117 context "issue reports" do
117 118 should_route :get, "/projects/567/issues/report", :controller => 'reports', :action => 'issue_report', :id => '567'
118 119 should_route :get, "/projects/567/issues/report/assigned_to", :controller => 'reports', :action => 'issue_report_details', :id => '567', :detail => 'assigned_to'
119 120 end
120 121
121 122 context "members" do
122 123 should_route :post, "/projects/5234/members/new", :controller => 'members', :action => 'new', :id => '5234'
123 124 end
124 125
125 126 context "messages" do
126 127 should_route :get, "/boards/22/topics/2", :controller => 'messages', :action => 'show', :id => '2', :board_id => '22'
127 128 should_route :get, "/boards/lala/topics/new", :controller => 'messages', :action => 'new', :board_id => 'lala'
128 129 should_route :get, "/boards/lala/topics/22/edit", :controller => 'messages', :action => 'edit', :id => '22', :board_id => 'lala'
129 130
130 131 should_route :post, "/boards/lala/topics/new", :controller => 'messages', :action => 'new', :board_id => 'lala'
131 132 should_route :post, "/boards/lala/topics/22/edit", :controller => 'messages', :action => 'edit', :id => '22', :board_id => 'lala'
132 133 should_route :post, "/boards/22/topics/555/replies", :controller => 'messages', :action => 'reply', :id => '555', :board_id => '22'
133 134 should_route :post, "/boards/22/topics/555/destroy", :controller => 'messages', :action => 'destroy', :id => '555', :board_id => '22'
134 135 end
135 136
136 137 context "news" do
137 138 should_route :get, "/news", :controller => 'news', :action => 'index'
138 139 should_route :get, "/news.atom", :controller => 'news', :action => 'index', :format => 'atom'
139 140 should_route :get, "/news.xml", :controller => 'news', :action => 'index', :format => 'xml'
140 141 should_route :get, "/news.json", :controller => 'news', :action => 'index', :format => 'json'
141 142 should_route :get, "/projects/567/news", :controller => 'news', :action => 'index', :project_id => '567'
142 143 should_route :get, "/projects/567/news.atom", :controller => 'news', :action => 'index', :format => 'atom', :project_id => '567'
143 144 should_route :get, "/projects/567/news.xml", :controller => 'news', :action => 'index', :format => 'xml', :project_id => '567'
144 145 should_route :get, "/projects/567/news.json", :controller => 'news', :action => 'index', :format => 'json', :project_id => '567'
145 146 should_route :get, "/news/2", :controller => 'news', :action => 'show', :id => '2'
146 147 should_route :get, "/projects/567/news/new", :controller => 'news', :action => 'new', :project_id => '567'
147 148 should_route :get, "/news/234", :controller => 'news', :action => 'show', :id => '234'
148 149
149 150 should_route :post, "/projects/567/news/new", :controller => 'news', :action => 'new', :project_id => '567'
150 151 should_route :post, "/news/567/edit", :controller => 'news', :action => 'edit', :id => '567'
151 152 should_route :post, "/news/567/destroy", :controller => 'news', :action => 'destroy', :id => '567'
152 153 end
153 154
154 155 context "projects" do
155 156 should_route :get, "/projects", :controller => 'projects', :action => 'index'
156 157 should_route :get, "/projects.atom", :controller => 'projects', :action => 'index', :format => 'atom'
157 158 should_route :get, "/projects.xml", :controller => 'projects', :action => 'index', :format => 'xml'
158 159 should_route :get, "/projects/new", :controller => 'projects', :action => 'add'
159 160 should_route :get, "/projects/test", :controller => 'projects', :action => 'show', :id => 'test'
160 161 should_route :get, "/projects/1.xml", :controller => 'projects', :action => 'show', :id => '1', :format => 'xml'
161 162 should_route :get, "/projects/4223/settings", :controller => 'projects', :action => 'settings', :id => '4223'
162 163 should_route :get, "/projects/4223/settings/members", :controller => 'projects', :action => 'settings', :id => '4223', :tab => 'members'
163 164 should_route :get, "/projects/567/destroy", :controller => 'projects', :action => 'destroy', :id => '567'
164 165 should_route :get, "/projects/33/files", :controller => 'projects', :action => 'list_files', :id => '33'
165 166 should_route :get, "/projects/33/files/new", :controller => 'projects', :action => 'add_file', :id => '33'
166 167 should_route :get, "/projects/33/roadmap", :controller => 'projects', :action => 'roadmap', :id => '33'
167 168 should_route :get, "/projects/33/activity", :controller => 'projects', :action => 'activity', :id => '33'
168 169 should_route :get, "/projects/33/activity.atom", :controller => 'projects', :action => 'activity', :id => '33', :format => 'atom'
169 170
170 171 should_route :post, "/projects/new", :controller => 'projects', :action => 'add'
171 172 should_route :post, "/projects.xml", :controller => 'projects', :action => 'add', :format => 'xml'
172 173 should_route :post, "/projects/4223/edit", :controller => 'projects', :action => 'edit', :id => '4223'
173 174 should_route :post, "/projects/64/destroy", :controller => 'projects', :action => 'destroy', :id => '64'
174 175 should_route :post, "/projects/33/files/new", :controller => 'projects', :action => 'add_file', :id => '33'
175 176 should_route :post, "/projects/64/archive", :controller => 'projects', :action => 'archive', :id => '64'
176 177 should_route :post, "/projects/64/unarchive", :controller => 'projects', :action => 'unarchive', :id => '64'
177 178 should_route :post, "/projects/64/activities/save", :controller => 'projects', :action => 'save_activities', :id => '64'
178 179
179 180 should_route :put, "/projects/1.xml", :controller => 'projects', :action => 'edit', :id => '1', :format => 'xml'
180 181
181 182 should_route :delete, "/projects/1.xml", :controller => 'projects', :action => 'destroy', :id => '1', :format => 'xml'
182 183 should_route :delete, "/projects/64/reset_activities", :controller => 'projects', :action => 'reset_activities', :id => '64'
183 184 end
184 185
185 186 context "repositories" do
186 187 should_route :get, "/projects/redmine/repository", :controller => 'repositories', :action => 'show', :id => 'redmine'
187 188 should_route :get, "/projects/redmine/repository/edit", :controller => 'repositories', :action => 'edit', :id => 'redmine'
188 189 should_route :get, "/projects/redmine/repository/revisions", :controller => 'repositories', :action => 'revisions', :id => 'redmine'
189 190 should_route :get, "/projects/redmine/repository/revisions.atom", :controller => 'repositories', :action => 'revisions', :id => 'redmine', :format => 'atom'
190 191 should_route :get, "/projects/redmine/repository/revisions/2457", :controller => 'repositories', :action => 'revision', :id => 'redmine', :rev => '2457'
191 192 should_route :get, "/projects/redmine/repository/revisions/2457/diff", :controller => 'repositories', :action => 'diff', :id => 'redmine', :rev => '2457'
192 193 should_route :get, "/projects/redmine/repository/revisions/2457/diff.diff", :controller => 'repositories', :action => 'diff', :id => 'redmine', :rev => '2457', :format => 'diff'
193 194 should_route :get, "/projects/redmine/repository/diff/path/to/file.c", :controller => 'repositories', :action => 'diff', :id => 'redmine', :path => %w[path to file.c]
194 195 should_route :get, "/projects/redmine/repository/revisions/2/diff/path/to/file.c", :controller => 'repositories', :action => 'diff', :id => 'redmine', :path => %w[path to file.c], :rev => '2'
195 196 should_route :get, "/projects/redmine/repository/browse/path/to/file.c", :controller => 'repositories', :action => 'browse', :id => 'redmine', :path => %w[path to file.c]
196 197 should_route :get, "/projects/redmine/repository/entry/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c]
197 198 should_route :get, "/projects/redmine/repository/revisions/2/entry/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c], :rev => '2'
198 199 should_route :get, "/projects/redmine/repository/raw/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c], :format => 'raw'
199 200 should_route :get, "/projects/redmine/repository/revisions/2/raw/path/to/file.c", :controller => 'repositories', :action => 'entry', :id => 'redmine', :path => %w[path to file.c], :rev => '2', :format => 'raw'
200 201 should_route :get, "/projects/redmine/repository/annotate/path/to/file.c", :controller => 'repositories', :action => 'annotate', :id => 'redmine', :path => %w[path to file.c]
201 202 should_route :get, "/projects/redmine/repository/changes/path/to/file.c", :controller => 'repositories', :action => 'changes', :id => 'redmine', :path => %w[path to file.c]
202 203 should_route :get, "/projects/redmine/repository/statistics", :controller => 'repositories', :action => 'stats', :id => 'redmine'
203 204
204 205
205 206 should_route :post, "/projects/redmine/repository/edit", :controller => 'repositories', :action => 'edit', :id => 'redmine'
206 207 end
207 208
208 209 context "timelogs" do
209 210 should_route :get, "/issues/567/time_entries/new", :controller => 'timelog', :action => 'edit', :issue_id => '567'
210 211 should_route :get, "/projects/ecookbook/time_entries/new", :controller => 'timelog', :action => 'edit', :project_id => 'ecookbook'
211 212 should_route :get, "/projects/ecookbook/issues/567/time_entries/new", :controller => 'timelog', :action => 'edit', :project_id => 'ecookbook', :issue_id => '567'
212 213 should_route :get, "/time_entries/22/edit", :controller => 'timelog', :action => 'edit', :id => '22'
213 214 should_route :get, "/time_entries/report", :controller => 'timelog', :action => 'report'
214 215 should_route :get, "/projects/567/time_entries/report", :controller => 'timelog', :action => 'report', :project_id => '567'
215 216 should_route :get, "/projects/567/time_entries/report.csv", :controller => 'timelog', :action => 'report', :project_id => '567', :format => 'csv'
216 217 should_route :get, "/time_entries", :controller => 'timelog', :action => 'details'
217 218 should_route :get, "/time_entries.csv", :controller => 'timelog', :action => 'details', :format => 'csv'
218 219 should_route :get, "/time_entries.atom", :controller => 'timelog', :action => 'details', :format => 'atom'
219 220 should_route :get, "/projects/567/time_entries", :controller => 'timelog', :action => 'details', :project_id => '567'
220 221 should_route :get, "/projects/567/time_entries.csv", :controller => 'timelog', :action => 'details', :project_id => '567', :format => 'csv'
221 222 should_route :get, "/projects/567/time_entries.atom", :controller => 'timelog', :action => 'details', :project_id => '567', :format => 'atom'
222 223 should_route :get, "/issues/234/time_entries", :controller => 'timelog', :action => 'details', :issue_id => '234'
223 224 should_route :get, "/issues/234/time_entries.csv", :controller => 'timelog', :action => 'details', :issue_id => '234', :format => 'csv'
224 225 should_route :get, "/issues/234/time_entries.atom", :controller => 'timelog', :action => 'details', :issue_id => '234', :format => 'atom'
225 226 should_route :get, "/projects/ecookbook/issues/123/time_entries", :controller => 'timelog', :action => 'details', :project_id => 'ecookbook', :issue_id => '123'
226 227
227 228 should_route :post, "/time_entries/55/destroy", :controller => 'timelog', :action => 'destroy', :id => '55'
228 229 end
229 230
230 231 context "users" do
231 232 should_route :get, "/users", :controller => 'users', :action => 'index'
232 233 should_route :get, "/users/44", :controller => 'users', :action => 'show', :id => '44'
233 234 should_route :get, "/users/new", :controller => 'users', :action => 'add'
234 235 should_route :get, "/users/444/edit", :controller => 'users', :action => 'edit', :id => '444'
235 236 should_route :get, "/users/222/edit/membership", :controller => 'users', :action => 'edit', :id => '222', :tab => 'membership'
236 237
237 238 should_route :post, "/users/new", :controller => 'users', :action => 'add'
238 239 should_route :post, "/users/444/edit", :controller => 'users', :action => 'edit', :id => '444'
239 240 should_route :post, "/users/123/memberships", :controller => 'users', :action => 'edit_membership', :id => '123'
240 241 should_route :post, "/users/123/memberships/55", :controller => 'users', :action => 'edit_membership', :id => '123', :membership_id => '55'
241 242 should_route :post, "/users/567/memberships/12/destroy", :controller => 'users', :action => 'destroy_membership', :id => '567', :membership_id => '12'
242 243 end
243 244
244 245 context "versions" do
245 246 should_route :get, "/projects/foo/versions/new", :controller => 'versions', :action => 'new', :project_id => 'foo'
246 247
247 248 should_route :post, "/projects/foo/versions/new", :controller => 'versions', :action => 'new', :project_id => 'foo'
248 249 end
249 250
250 251 context "wiki (singular, project's pages)" do
251 252 should_route :get, "/projects/567/wiki", :controller => 'wiki', :action => 'index', :id => '567'
252 253 should_route :get, "/projects/567/wiki/lalala", :controller => 'wiki', :action => 'index', :id => '567', :page => 'lalala'
253 254 should_route :get, "/projects/567/wiki/my_page/edit", :controller => 'wiki', :action => 'edit', :id => '567', :page => 'my_page'
254 255 should_route :get, "/projects/1/wiki/CookBook_documentation/history", :controller => 'wiki', :action => 'history', :id => '1', :page => 'CookBook_documentation'
255 256 should_route :get, "/projects/1/wiki/CookBook_documentation/diff/2/vs/1", :controller => 'wiki', :action => 'diff', :id => '1', :page => 'CookBook_documentation', :version => '2', :version_from => '1'
256 257 should_route :get, "/projects/1/wiki/CookBook_documentation/annotate/2", :controller => 'wiki', :action => 'annotate', :id => '1', :page => 'CookBook_documentation', :version => '2'
257 258 should_route :get, "/projects/22/wiki/ladida/rename", :controller => 'wiki', :action => 'rename', :id => '22', :page => 'ladida'
258 259 should_route :get, "/projects/567/wiki/page_index", :controller => 'wiki', :action => 'special', :id => '567', :page => 'page_index'
259 260 should_route :get, "/projects/567/wiki/Page_Index", :controller => 'wiki', :action => 'special', :id => '567', :page => 'Page_Index'
260 261 should_route :get, "/projects/567/wiki/date_index", :controller => 'wiki', :action => 'special', :id => '567', :page => 'date_index'
261 262 should_route :get, "/projects/567/wiki/export", :controller => 'wiki', :action => 'special', :id => '567', :page => 'export'
262 263
263 264 should_route :post, "/projects/567/wiki/my_page/edit", :controller => 'wiki', :action => 'edit', :id => '567', :page => 'my_page'
264 265 should_route :post, "/projects/567/wiki/CookBook_documentation/preview", :controller => 'wiki', :action => 'preview', :id => '567', :page => 'CookBook_documentation'
265 266 should_route :post, "/projects/22/wiki/ladida/rename", :controller => 'wiki', :action => 'rename', :id => '22', :page => 'ladida'
266 267 should_route :post, "/projects/22/wiki/ladida/destroy", :controller => 'wiki', :action => 'destroy', :id => '22', :page => 'ladida'
267 268 should_route :post, "/projects/22/wiki/ladida/protect", :controller => 'wiki', :action => 'protect', :id => '22', :page => 'ladida'
268 269 end
269 270
270 271 context "wikis (plural, admin setup)" do
271 272 should_route :get, "/projects/ladida/wiki/destroy", :controller => 'wikis', :action => 'destroy', :id => 'ladida'
272 273
273 274 should_route :post, "/projects/ladida/wiki", :controller => 'wikis', :action => 'edit', :id => 'ladida'
274 275 should_route :post, "/projects/ladida/wiki/destroy", :controller => 'wikis', :action => 'destroy', :id => 'ladida'
275 276 end
276 277
277 278 context "administration panel" do
278 279 should_route :get, "/admin/projects", :controller => 'admin', :action => 'projects'
279 280 end
280 281 end
General Comments 0
You need to be logged in to leave comments. Login now