##// END OF EJS Templates
New issue raises an error if no projects available (#23410)....
Jean-Philippe Lang -
r15358:011606745fc7
parent child
Show More
@@ -1,548 +1,545
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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 default_search_scope :issues
20 20
21 21 before_action :find_issue, :only => [:show, :edit, :update]
22 22 before_action :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
23 23 before_action :authorize, :except => [:index, :new, :create]
24 24 before_action :find_optional_project, :only => [:index, :new, :create]
25 25 before_action :build_new_issue_from_params, :only => [:new, :create]
26 26 accept_rss_auth :index, :show
27 27 accept_api_auth :index, :show, :create, :update, :destroy
28 28
29 29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
30 30
31 31 helper :journals
32 32 helper :projects
33 33 helper :custom_fields
34 34 helper :issue_relations
35 35 helper :watchers
36 36 helper :attachments
37 37 helper :queries
38 38 include QueriesHelper
39 39 helper :repositories
40 40 helper :sort
41 41 include SortHelper
42 42 helper :timelog
43 43
44 44 def index
45 45 retrieve_query
46 46 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
47 47 sort_update(@query.sortable_columns)
48 48 @query.sort_criteria = sort_criteria.to_a
49 49
50 50 if @query.valid?
51 51 case params[:format]
52 52 when 'csv', 'pdf'
53 53 @limit = Setting.issues_export_limit.to_i
54 54 if params[:columns] == 'all'
55 55 @query.column_names = @query.available_inline_columns.map(&:name)
56 56 end
57 57 when 'atom'
58 58 @limit = Setting.feeds_limit.to_i
59 59 when 'xml', 'json'
60 60 @offset, @limit = api_offset_and_limit
61 61 @query.column_names = %w(author)
62 62 else
63 63 @limit = per_page_option
64 64 end
65 65
66 66 @issue_count = @query.issue_count
67 67 @issue_pages = Paginator.new @issue_count, @limit, params['page']
68 68 @offset ||= @issue_pages.offset
69 69 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
70 70 :order => sort_clause,
71 71 :offset => @offset,
72 72 :limit => @limit)
73 73 @issue_count_by_group = @query.issue_count_by_group
74 74
75 75 respond_to do |format|
76 76 format.html { render :template => 'issues/index', :layout => !request.xhr? }
77 77 format.api {
78 78 Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
79 79 }
80 80 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
81 81 format.csv { send_data(query_to_csv(@issues, @query, params[:csv]), :type => 'text/csv; header=present', :filename => 'issues.csv') }
82 82 format.pdf { send_file_headers! :type => 'application/pdf', :filename => 'issues.pdf' }
83 83 end
84 84 else
85 85 respond_to do |format|
86 86 format.html { render(:template => 'issues/index', :layout => !request.xhr?) }
87 87 format.any(:atom, :csv, :pdf) { head 422 }
88 88 format.api { render_validation_errors(@query) }
89 89 end
90 90 end
91 91 rescue ActiveRecord::RecordNotFound
92 92 render_404
93 93 end
94 94
95 95 def show
96 96 @journals = @issue.journals.
97 97 preload(:details).
98 98 preload(:user => :email_address).
99 99 reorder(:created_on, :id).to_a
100 100 @journals.each_with_index {|j,i| j.indice = i+1}
101 101 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
102 102 Journal.preload_journals_details_custom_fields(@journals)
103 103 @journals.select! {|journal| journal.notes? || journal.visible_details.any?}
104 104 @journals.reverse! if User.current.wants_comments_in_reverse_order?
105 105
106 106 @changesets = @issue.changesets.visible.preload(:repository, :user).to_a
107 107 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
108 108
109 109 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
110 110 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
111 111 @priorities = IssuePriority.active
112 112 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
113 113 @relation = IssueRelation.new
114 114
115 115 respond_to do |format|
116 116 format.html {
117 117 retrieve_previous_and_next_issue_ids
118 118 render :template => 'issues/show'
119 119 }
120 120 format.api
121 121 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
122 122 format.pdf {
123 123 send_file_headers! :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf"
124 124 }
125 125 end
126 126 end
127 127
128 128 def new
129 129 respond_to do |format|
130 130 format.html { render :action => 'new', :layout => !request.xhr? }
131 131 format.js
132 132 end
133 133 end
134 134
135 135 def create
136 136 unless User.current.allowed_to?(:add_issues, @issue.project, :global => true)
137 137 raise ::Unauthorized
138 138 end
139 139 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
140 140 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
141 141 if @issue.save
142 142 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
143 143 respond_to do |format|
144 144 format.html {
145 145 render_attachment_warning_if_needed(@issue)
146 146 flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
147 147 redirect_after_create
148 148 }
149 149 format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
150 150 end
151 151 return
152 152 else
153 153 respond_to do |format|
154 format.html {
155 if @issue.project.nil?
156 render_error :status => 422
157 else
158 render :action => 'new'
159 end
160 }
154 format.html { render :action => 'new' }
161 155 format.api { render_validation_errors(@issue) }
162 156 end
163 157 end
164 158 end
165 159
166 160 def edit
167 161 return unless update_issue_from_params
168 162
169 163 respond_to do |format|
170 164 format.html { }
171 165 format.js
172 166 end
173 167 end
174 168
175 169 def update
176 170 return unless update_issue_from_params
177 171 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
178 172 saved = false
179 173 begin
180 174 saved = save_issue_with_child_records
181 175 rescue ActiveRecord::StaleObjectError
182 176 @conflict = true
183 177 if params[:last_journal_id]
184 178 @conflict_journals = @issue.journals_after(params[:last_journal_id]).to_a
185 179 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
186 180 end
187 181 end
188 182
189 183 if saved
190 184 render_attachment_warning_if_needed(@issue)
191 185 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
192 186
193 187 respond_to do |format|
194 188 format.html { redirect_back_or_default issue_path(@issue, previous_and_next_issue_ids_params) }
195 189 format.api { render_api_ok }
196 190 end
197 191 else
198 192 respond_to do |format|
199 193 format.html { render :action => 'edit' }
200 194 format.api { render_validation_errors(@issue) }
201 195 end
202 196 end
203 197 end
204 198
205 199 # Bulk edit/copy a set of issues
206 200 def bulk_edit
207 201 @issues.sort!
208 202 @copy = params[:copy].present?
209 203 @notes = params[:notes]
210 204
211 205 if @copy
212 206 unless User.current.allowed_to?(:copy_issues, @projects)
213 207 raise ::Unauthorized
214 208 end
215 209 else
216 210 unless @issues.all?(&:attributes_editable?)
217 211 raise ::Unauthorized
218 212 end
219 213 end
220 214
221 215 @allowed_projects = Issue.allowed_target_projects
222 216 if params[:issue]
223 217 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
224 218 if @target_project
225 219 target_projects = [@target_project]
226 220 end
227 221 end
228 222 target_projects ||= @projects
229 223
230 224 if @copy
231 225 # Copied issues will get their default statuses
232 226 @available_statuses = []
233 227 else
234 228 @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
235 229 end
236 230 @custom_fields = @issues.map{|i|i.editable_custom_fields}.reduce(:&)
237 231 @assignables = target_projects.map(&:assignable_users).reduce(:&)
238 232 @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p) }.reduce(:&)
239 233 @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
240 234 @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
241 235 if @copy
242 236 @attachments_present = @issues.detect {|i| i.attachments.any?}.present?
243 237 @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
244 238 end
245 239
246 240 @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
247 241
248 242 @issue_params = params[:issue] || {}
249 243 @issue_params[:custom_field_values] ||= {}
250 244 end
251 245
252 246 def bulk_update
253 247 @issues.sort!
254 248 @copy = params[:copy].present?
255 249
256 250 attributes = parse_params_for_bulk_update(params[:issue])
257 251 copy_subtasks = (params[:copy_subtasks] == '1')
258 252 copy_attachments = (params[:copy_attachments] == '1')
259 253
260 254 if @copy
261 255 unless User.current.allowed_to?(:copy_issues, @projects)
262 256 raise ::Unauthorized
263 257 end
264 258 target_projects = @projects
265 259 if attributes['project_id'].present?
266 260 target_projects = Project.where(:id => attributes['project_id']).to_a
267 261 end
268 262 unless User.current.allowed_to?(:add_issues, target_projects)
269 263 raise ::Unauthorized
270 264 end
271 265 else
272 266 unless @issues.all?(&:attributes_editable?)
273 267 raise ::Unauthorized
274 268 end
275 269 end
276 270
277 271 unsaved_issues = []
278 272 saved_issues = []
279 273
280 274 if @copy && copy_subtasks
281 275 # Descendant issues will be copied with the parent task
282 276 # Don't copy them twice
283 277 @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
284 278 end
285 279
286 280 @issues.each do |orig_issue|
287 281 orig_issue.reload
288 282 if @copy
289 283 issue = orig_issue.copy({},
290 284 :attachments => copy_attachments,
291 285 :subtasks => copy_subtasks,
292 286 :link => link_copy?(params[:link_copy])
293 287 )
294 288 else
295 289 issue = orig_issue
296 290 end
297 291 journal = issue.init_journal(User.current, params[:notes])
298 292 issue.safe_attributes = attributes
299 293 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
300 294 if issue.save
301 295 saved_issues << issue
302 296 else
303 297 unsaved_issues << orig_issue
304 298 end
305 299 end
306 300
307 301 if unsaved_issues.empty?
308 302 flash[:notice] = l(:notice_successful_update) unless saved_issues.empty?
309 303 if params[:follow]
310 304 if @issues.size == 1 && saved_issues.size == 1
311 305 redirect_to issue_path(saved_issues.first)
312 306 elsif saved_issues.map(&:project).uniq.size == 1
313 307 redirect_to project_issues_path(saved_issues.map(&:project).first)
314 308 end
315 309 else
316 310 redirect_back_or_default _project_issues_path(@project)
317 311 end
318 312 else
319 313 @saved_issues = @issues
320 314 @unsaved_issues = unsaved_issues
321 315 @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a
322 316 bulk_edit
323 317 render :action => 'bulk_edit'
324 318 end
325 319 end
326 320
327 321 def destroy
328 322 raise Unauthorized unless @issues.all?(&:deletable?)
329 323 @hours = TimeEntry.where(:issue_id => @issues.map(&:id)).sum(:hours).to_f
330 324 if @hours > 0
331 325 case params[:todo]
332 326 when 'destroy'
333 327 # nothing to do
334 328 when 'nullify'
335 329 TimeEntry.where(['issue_id IN (?)', @issues]).update_all('issue_id = NULL')
336 330 when 'reassign'
337 331 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
338 332 if reassign_to.nil?
339 333 flash.now[:error] = l(:error_issue_not_found_in_project)
340 334 return
341 335 else
342 336 TimeEntry.where(['issue_id IN (?)', @issues]).
343 337 update_all("issue_id = #{reassign_to.id}")
344 338 end
345 339 else
346 340 # display the destroy form if it's a user request
347 341 return unless api_request?
348 342 end
349 343 end
350 344 @issues.each do |issue|
351 345 begin
352 346 issue.reload.destroy
353 347 rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
354 348 # nothing to do, issue was already deleted (eg. by a parent)
355 349 end
356 350 end
357 351 respond_to do |format|
358 352 format.html { redirect_back_or_default _project_issues_path(@project) }
359 353 format.api { render_api_ok }
360 354 end
361 355 end
362 356
363 357 # Overrides Redmine::MenuManager::MenuController::ClassMethods for
364 358 # when the "New issue" tab is enabled
365 359 def current_menu_item
366 360 if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym)
367 361 :new_issue
368 362 else
369 363 super
370 364 end
371 365 end
372 366
373 367 private
374 368
375 369 def retrieve_previous_and_next_issue_ids
376 370 if params[:prev_issue_id].present? || params[:next_issue_id].present?
377 371 @prev_issue_id = params[:prev_issue_id].presence.try(:to_i)
378 372 @next_issue_id = params[:next_issue_id].presence.try(:to_i)
379 373 @issue_position = params[:issue_position].presence.try(:to_i)
380 374 @issue_count = params[:issue_count].presence.try(:to_i)
381 375 else
382 376 retrieve_query_from_session
383 377 if @query
384 378 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
385 379 sort_update(@query.sortable_columns, 'issues_index_sort')
386 380 limit = 500
387 381 issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])
388 382 if (idx = issue_ids.index(@issue.id)) && idx < limit
389 383 if issue_ids.size < 500
390 384 @issue_position = idx + 1
391 385 @issue_count = issue_ids.size
392 386 end
393 387 @prev_issue_id = issue_ids[idx - 1] if idx > 0
394 388 @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
395 389 end
396 390 end
397 391 end
398 392 end
399 393
400 394 def previous_and_next_issue_ids_params
401 395 {
402 396 :prev_issue_id => params[:prev_issue_id],
403 397 :next_issue_id => params[:next_issue_id],
404 398 :issue_position => params[:issue_position],
405 399 :issue_count => params[:issue_count]
406 400 }.reject {|k,v| k.blank?}
407 401 end
408 402
409 403 # Used by #edit and #update to set some common instance variables
410 404 # from the params
411 405 def update_issue_from_params
412 406 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
413 407 if params[:time_entry]
414 408 @time_entry.safe_attributes = params[:time_entry]
415 409 end
416 410
417 411 @issue.init_journal(User.current)
418 412
419 413 issue_attributes = params[:issue]
420 414 if issue_attributes && params[:conflict_resolution]
421 415 case params[:conflict_resolution]
422 416 when 'overwrite'
423 417 issue_attributes = issue_attributes.dup
424 418 issue_attributes.delete(:lock_version)
425 419 when 'add_notes'
426 420 issue_attributes = issue_attributes.slice(:notes, :private_notes)
427 421 when 'cancel'
428 422 redirect_to issue_path(@issue)
429 423 return false
430 424 end
431 425 end
432 426 @issue.safe_attributes = issue_attributes
433 427 @priorities = IssuePriority.active
434 428 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
435 429 true
436 430 end
437 431
438 432 # Used by #new and #create to build a new issue from the params
439 433 # The new issue will be copied from an existing one if copy_from parameter is given
440 434 def build_new_issue_from_params
441 435 @issue = Issue.new
442 436 if params[:copy_from]
443 437 begin
444 438 @issue.init_journal(User.current)
445 439 @copy_from = Issue.visible.find(params[:copy_from])
446 440 unless User.current.allowed_to?(:copy_issues, @copy_from.project)
447 441 raise ::Unauthorized
448 442 end
449 443 @link_copy = link_copy?(params[:link_copy]) || request.get?
450 444 @copy_attachments = params[:copy_attachments].present? || request.get?
451 445 @copy_subtasks = params[:copy_subtasks].present? || request.get?
452 446 @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks, :link => @link_copy)
453 447 @issue.parent_issue_id = @copy_from.parent_id
454 448 rescue ActiveRecord::RecordNotFound
455 449 render_404
456 450 return
457 451 end
458 452 end
459 453 @issue.project = @project
460 454 if request.get?
461 455 @issue.project ||= @issue.allowed_target_projects.first
462 456 end
463 457 @issue.author ||= User.current
464 458 @issue.start_date ||= User.current.today if Setting.default_issue_start_date_to_creation_date?
465 459
466 460 attrs = (params[:issue] || {}).deep_dup
467 461 if action_name == 'new' && params[:was_default_status] == attrs[:status_id]
468 462 attrs.delete(:status_id)
469 463 end
470 464 if action_name == 'new' && params[:form_update_triggered_by] == 'issue_project_id'
471 465 # Discard submitted version when changing the project on the issue form
472 466 # so we can use the default version for the new project
473 467 attrs.delete(:fixed_version_id)
474 468 end
475 469 @issue.safe_attributes = attrs
476 470
477 471 if @issue.project
478 472 @issue.tracker ||= @issue.allowed_target_trackers.first
479 473 if @issue.tracker.nil?
480 474 if @issue.project.trackers.any?
481 475 # None of the project trackers is allowed to the user
482 476 render_error :message => l(:error_no_tracker_allowed_for_new_issue_in_project), :status => 403
483 477 else
484 478 # Project has no trackers
485 479 render_error l(:error_no_tracker_in_project)
486 480 end
487 481 return false
488 482 end
489 483 if @issue.status.nil?
490 484 render_error l(:error_no_default_issue_status)
491 485 return false
492 486 end
487 else
488 render_error :message => l(:error_no_projects_with_tracker_allowed_for_new_issue), :status => 403
489 return false
493 490 end
494 491
495 492 @priorities = IssuePriority.active
496 493 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
497 494 end
498 495
499 496 # Saves @issue and a time_entry from the parameters
500 497 def save_issue_with_child_records
501 498 Issue.transaction do
502 499 if params[:time_entry] && (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) && User.current.allowed_to?(:log_time, @issue.project)
503 500 time_entry = @time_entry || TimeEntry.new
504 501 time_entry.project = @issue.project
505 502 time_entry.issue = @issue
506 503 time_entry.user = User.current
507 504 time_entry.spent_on = User.current.today
508 505 time_entry.attributes = params[:time_entry]
509 506 @issue.time_entries << time_entry
510 507 end
511 508
512 509 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
513 510 if @issue.save
514 511 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
515 512 else
516 513 raise ActiveRecord::Rollback
517 514 end
518 515 end
519 516 end
520 517
521 518 # Returns true if the issue copy should be linked
522 519 # to the original issue
523 520 def link_copy?(param)
524 521 case Setting.link_copied_issue
525 522 when 'yes'
526 523 true
527 524 when 'no'
528 525 false
529 526 when 'ask'
530 527 param == '1'
531 528 end
532 529 end
533 530
534 531 # Redirects user after a successful issue creation
535 532 def redirect_after_create
536 533 if params[:continue]
537 534 attrs = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?}
538 535 if params[:project_id]
539 536 redirect_to new_project_issue_path(@issue.project, :issue => attrs)
540 537 else
541 538 attrs.merge! :project_id => @issue.project_id
542 539 redirect_to new_issue_path(:issue => attrs)
543 540 end
544 541 else
545 542 redirect_to issue_path(@issue)
546 543 end
547 544 end
548 545 end
@@ -1,1193 +1,1194
1 1 en:
2 2 # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl)
3 3 direction: ltr
4 4 date:
5 5 formats:
6 6 # Use the strftime parameters for formats.
7 7 # When no format has been given, it uses default.
8 8 # You can provide other formats here if you like!
9 9 default: "%m/%d/%Y"
10 10 short: "%b %d"
11 11 long: "%B %d, %Y"
12 12
13 13 day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
14 14 abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
15 15
16 16 # Don't forget the nil at the beginning; there's no such thing as a 0th month
17 17 month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
18 18 abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
19 19 # Used in date_select and datime_select.
20 20 order:
21 21 - :year
22 22 - :month
23 23 - :day
24 24
25 25 time:
26 26 formats:
27 27 default: "%m/%d/%Y %I:%M %p"
28 28 time: "%I:%M %p"
29 29 short: "%d %b %H:%M"
30 30 long: "%B %d, %Y %H:%M"
31 31 am: "am"
32 32 pm: "pm"
33 33
34 34 datetime:
35 35 distance_in_words:
36 36 half_a_minute: "half a minute"
37 37 less_than_x_seconds:
38 38 one: "less than 1 second"
39 39 other: "less than %{count} seconds"
40 40 x_seconds:
41 41 one: "1 second"
42 42 other: "%{count} seconds"
43 43 less_than_x_minutes:
44 44 one: "less than a minute"
45 45 other: "less than %{count} minutes"
46 46 x_minutes:
47 47 one: "1 minute"
48 48 other: "%{count} minutes"
49 49 about_x_hours:
50 50 one: "about 1 hour"
51 51 other: "about %{count} hours"
52 52 x_hours:
53 53 one: "1 hour"
54 54 other: "%{count} hours"
55 55 x_days:
56 56 one: "1 day"
57 57 other: "%{count} days"
58 58 about_x_months:
59 59 one: "about 1 month"
60 60 other: "about %{count} months"
61 61 x_months:
62 62 one: "1 month"
63 63 other: "%{count} months"
64 64 about_x_years:
65 65 one: "about 1 year"
66 66 other: "about %{count} years"
67 67 over_x_years:
68 68 one: "over 1 year"
69 69 other: "over %{count} years"
70 70 almost_x_years:
71 71 one: "almost 1 year"
72 72 other: "almost %{count} years"
73 73
74 74 number:
75 75 format:
76 76 separator: "."
77 77 delimiter: ""
78 78 precision: 3
79 79
80 80 human:
81 81 format:
82 82 delimiter: ""
83 83 precision: 3
84 84 storage_units:
85 85 format: "%n %u"
86 86 units:
87 87 byte:
88 88 one: "Byte"
89 89 other: "Bytes"
90 90 kb: "KB"
91 91 mb: "MB"
92 92 gb: "GB"
93 93 tb: "TB"
94 94
95 95 # Used in array.to_sentence.
96 96 support:
97 97 array:
98 98 sentence_connector: "and"
99 99 skip_last_comma: false
100 100
101 101 activerecord:
102 102 errors:
103 103 template:
104 104 header:
105 105 one: "1 error prohibited this %{model} from being saved"
106 106 other: "%{count} errors prohibited this %{model} from being saved"
107 107 messages:
108 108 inclusion: "is not included in the list"
109 109 exclusion: "is reserved"
110 110 invalid: "is invalid"
111 111 confirmation: "doesn't match confirmation"
112 112 accepted: "must be accepted"
113 113 empty: "cannot be empty"
114 114 blank: "cannot be blank"
115 115 too_long: "is too long (maximum is %{count} characters)"
116 116 too_short: "is too short (minimum is %{count} characters)"
117 117 wrong_length: "is the wrong length (should be %{count} characters)"
118 118 taken: "has already been taken"
119 119 not_a_number: "is not a number"
120 120 not_a_date: "is not a valid date"
121 121 greater_than: "must be greater than %{count}"
122 122 greater_than_or_equal_to: "must be greater than or equal to %{count}"
123 123 equal_to: "must be equal to %{count}"
124 124 less_than: "must be less than %{count}"
125 125 less_than_or_equal_to: "must be less than or equal to %{count}"
126 126 odd: "must be odd"
127 127 even: "must be even"
128 128 greater_than_start_date: "must be greater than start date"
129 129 not_same_project: "doesn't belong to the same project"
130 130 circular_dependency: "This relation would create a circular dependency"
131 131 cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks"
132 132 earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
133 133
134 134 actionview_instancetag_blank_option: Please select
135 135
136 136 general_text_No: 'No'
137 137 general_text_Yes: 'Yes'
138 138 general_text_no: 'no'
139 139 general_text_yes: 'yes'
140 140 general_lang_name: 'English'
141 141 general_csv_separator: ','
142 142 general_csv_decimal_separator: '.'
143 143 general_csv_encoding: ISO-8859-1
144 144 general_pdf_fontname: freesans
145 145 general_pdf_monospaced_fontname: freemono
146 146 general_first_day_of_week: '7'
147 147
148 148 notice_account_updated: Account was successfully updated.
149 149 notice_account_invalid_credentials: Invalid user or password
150 150 notice_account_password_updated: Password was successfully updated.
151 151 notice_account_wrong_password: Wrong password
152 152 notice_account_register_done: Account was successfully created. An email containing the instructions to activate your account was sent to %{email}.
153 153 notice_account_unknown_email: Unknown user.
154 154 notice_account_not_activated_yet: You haven't activated your account yet. If you want to receive a new activation email, please <a href="%{url}">click this link</a>.
155 155 notice_account_locked: Your account is locked.
156 156 notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
157 157 notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
158 158 notice_account_activated: Your account has been activated. You can now log in.
159 159 notice_successful_create: Successful creation.
160 160 notice_successful_update: Successful update.
161 161 notice_successful_delete: Successful deletion.
162 162 notice_successful_connection: Successful connection.
163 163 notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
164 164 notice_locking_conflict: Data has been updated by another user.
165 165 notice_not_authorized: You are not authorized to access this page.
166 166 notice_not_authorized_archived_project: The project you're trying to access has been archived.
167 167 notice_email_sent: "An email was sent to %{value}"
168 168 notice_email_error: "An error occurred while sending mail (%{value})"
169 169 notice_feeds_access_key_reseted: Your Atom access key was reset.
170 170 notice_api_access_key_reseted: Your API access key was reset.
171 171 notice_failed_to_save_issues: "Failed to save %{count} issue(s) on %{total} selected: %{ids}."
172 172 notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}."
173 173 notice_failed_to_save_members: "Failed to save member(s): %{errors}."
174 174 notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
175 175 notice_account_pending: "Your account was created and is now pending administrator approval."
176 176 notice_default_data_loaded: Default configuration successfully loaded.
177 177 notice_unable_delete_version: Unable to delete version.
178 178 notice_unable_delete_time_entry: Unable to delete time log entry.
179 179 notice_issue_done_ratios_updated: Issue done ratios updated.
180 180 notice_gantt_chart_truncated: "The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})"
181 181 notice_issue_successful_create: "Issue %{id} created."
182 182 notice_issue_update_conflict: "The issue has been updated by an other user while you were editing it."
183 183 notice_account_deleted: "Your account has been permanently deleted."
184 184 notice_user_successful_create: "User %{id} created."
185 185 notice_new_password_must_be_different: The new password must be different from the current password
186 186 notice_import_finished: "%{count} items have been imported"
187 187 notice_import_finished_with_errors: "%{count} out of %{total} items could not be imported"
188 188
189 189 error_can_t_load_default_data: "Default configuration could not be loaded: %{value}"
190 190 error_scm_not_found: "The entry or revision was not found in the repository."
191 191 error_scm_command_failed: "An error occurred when trying to access the repository: %{value}"
192 192 error_scm_annotate: "The entry does not exist or cannot be annotated."
193 193 error_scm_annotate_big_text_file: "The entry cannot be annotated, as it exceeds the maximum text file size."
194 194 error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
195 195 error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.'
196 196 error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
197 197 error_can_not_delete_custom_field: Unable to delete custom field
198 198 error_can_not_delete_tracker: "This tracker contains issues and cannot be deleted."
199 199 error_can_not_remove_role: "This role is in use and cannot be deleted."
200 200 error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version cannot be reopened'
201 201 error_can_not_archive_project: This project cannot be archived
202 202 error_issue_done_ratios_not_updated: "Issue done ratios not updated."
203 203 error_workflow_copy_source: 'Please select a source tracker or role'
204 204 error_workflow_copy_target: 'Please select target tracker(s) and role(s)'
205 205 error_unable_delete_issue_status: 'Unable to delete issue status'
206 206 error_unable_to_connect: "Unable to connect (%{value})"
207 207 error_attachment_too_big: "This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size})"
208 208 error_session_expired: "Your session has expired. Please login again."
209 209 warning_attachments_not_saved: "%{count} file(s) could not be saved."
210 210 error_password_expired: "Your password has expired or the administrator requires you to change it."
211 211 error_invalid_file_encoding: "The file is not a valid %{encoding} encoded file"
212 212 error_invalid_csv_file_or_settings: "The file is not a CSV file or does not match the settings below"
213 213 error_can_not_read_import_file: "An error occurred while reading the file to import"
214 214 error_attachment_extension_not_allowed: "Attachment extension %{extension} is not allowed"
215 215 error_ldap_bind_credentials: "Invalid LDAP Account/Password"
216 216 error_no_tracker_allowed_for_new_issue_in_project: "The project doesn't have any trackers for which you can create an issue"
217 error_no_projects_with_tracker_allowed_for_new_issue: "There are no projects with trackers for which you can create an issue"
217 218
218 219 mail_subject_lost_password: "Your %{value} password"
219 220 mail_body_lost_password: 'To change your password, click on the following link:'
220 221 mail_subject_register: "Your %{value} account activation"
221 222 mail_body_register: 'To activate your account, click on the following link:'
222 223 mail_body_account_information_external: "You can use your %{value} account to log in."
223 224 mail_body_account_information: Your account information
224 225 mail_subject_account_activation_request: "%{value} account activation request"
225 226 mail_body_account_activation_request: "A new user (%{value}) has registered. The account is pending your approval:"
226 227 mail_subject_reminder: "%{count} issue(s) due in the next %{days} days"
227 228 mail_body_reminder: "%{count} issue(s) that are assigned to you are due in the next %{days} days:"
228 229 mail_subject_wiki_content_added: "'%{id}' wiki page has been added"
229 230 mail_body_wiki_content_added: "The '%{id}' wiki page has been added by %{author}."
230 231 mail_subject_wiki_content_updated: "'%{id}' wiki page has been updated"
231 232 mail_body_wiki_content_updated: "The '%{id}' wiki page has been updated by %{author}."
232 233 mail_subject_security_notification: "Security notification"
233 234 mail_body_security_notification_change: "%{field} was changed."
234 235 mail_body_security_notification_change_to: "%{field} was changed to %{value}."
235 236 mail_body_security_notification_add: "%{field} %{value} was added."
236 237 mail_body_security_notification_remove: "%{field} %{value} was removed."
237 238 mail_body_security_notification_notify_enabled: "Email address %{value} now receives notifications."
238 239 mail_body_security_notification_notify_disabled: "Email address %{value} no longer receives notifications."
239 240 mail_body_settings_updated: "The following settings were changed:"
240 241 mail_body_password_updated: "Your password has been changed."
241 242
242 243 field_name: Name
243 244 field_description: Description
244 245 field_summary: Summary
245 246 field_is_required: Required
246 247 field_firstname: First name
247 248 field_lastname: Last name
248 249 field_mail: Email
249 250 field_address: Email
250 251 field_filename: File
251 252 field_filesize: Size
252 253 field_downloads: Downloads
253 254 field_author: Author
254 255 field_created_on: Created
255 256 field_updated_on: Updated
256 257 field_closed_on: Closed
257 258 field_field_format: Format
258 259 field_is_for_all: For all projects
259 260 field_possible_values: Possible values
260 261 field_regexp: Regular expression
261 262 field_min_length: Minimum length
262 263 field_max_length: Maximum length
263 264 field_value: Value
264 265 field_category: Category
265 266 field_title: Title
266 267 field_project: Project
267 268 field_issue: Issue
268 269 field_status: Status
269 270 field_notes: Notes
270 271 field_is_closed: Issue closed
271 272 field_is_default: Default value
272 273 field_tracker: Tracker
273 274 field_subject: Subject
274 275 field_due_date: Due date
275 276 field_assigned_to: Assignee
276 277 field_priority: Priority
277 278 field_fixed_version: Target version
278 279 field_user: User
279 280 field_principal: Principal
280 281 field_role: Role
281 282 field_homepage: Homepage
282 283 field_is_public: Public
283 284 field_parent: Subproject of
284 285 field_is_in_roadmap: Issues displayed in roadmap
285 286 field_login: Login
286 287 field_mail_notification: Email notifications
287 288 field_admin: Administrator
288 289 field_last_login_on: Last connection
289 290 field_language: Language
290 291 field_effective_date: Due date
291 292 field_password: Password
292 293 field_new_password: New password
293 294 field_password_confirmation: Confirmation
294 295 field_version: Version
295 296 field_type: Type
296 297 field_host: Host
297 298 field_port: Port
298 299 field_account: Account
299 300 field_base_dn: Base DN
300 301 field_attr_login: Login attribute
301 302 field_attr_firstname: Firstname attribute
302 303 field_attr_lastname: Lastname attribute
303 304 field_attr_mail: Email attribute
304 305 field_onthefly: On-the-fly user creation
305 306 field_start_date: Start date
306 307 field_done_ratio: "% Done"
307 308 field_auth_source: Authentication mode
308 309 field_hide_mail: Hide my email address
309 310 field_comments: Comment
310 311 field_url: URL
311 312 field_start_page: Start page
312 313 field_subproject: Subproject
313 314 field_hours: Hours
314 315 field_activity: Activity
315 316 field_spent_on: Date
316 317 field_identifier: Identifier
317 318 field_is_filter: Used as a filter
318 319 field_issue_to: Related issue
319 320 field_delay: Delay
320 321 field_assignable: Issues can be assigned to this role
321 322 field_redirect_existing_links: Redirect existing links
322 323 field_estimated_hours: Estimated time
323 324 field_column_names: Columns
324 325 field_time_entries: Log time
325 326 field_time_zone: Time zone
326 327 field_searchable: Searchable
327 328 field_default_value: Default value
328 329 field_comments_sorting: Display comments
329 330 field_parent_title: Parent page
330 331 field_editable: Editable
331 332 field_watcher: Watcher
332 333 field_identity_url: OpenID URL
333 334 field_content: Content
334 335 field_group_by: Group results by
335 336 field_sharing: Sharing
336 337 field_parent_issue: Parent task
337 338 field_member_of_group: "Assignee's group"
338 339 field_assigned_to_role: "Assignee's role"
339 340 field_text: Text field
340 341 field_visible: Visible
341 342 field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text"
342 343 field_issues_visibility: Issues visibility
343 344 field_is_private: Private
344 345 field_commit_logs_encoding: Commit messages encoding
345 346 field_scm_path_encoding: Path encoding
346 347 field_path_to_repository: Path to repository
347 348 field_root_directory: Root directory
348 349 field_cvsroot: CVSROOT
349 350 field_cvs_module: Module
350 351 field_repository_is_default: Main repository
351 352 field_multiple: Multiple values
352 353 field_auth_source_ldap_filter: LDAP filter
353 354 field_core_fields: Standard fields
354 355 field_timeout: "Timeout (in seconds)"
355 356 field_board_parent: Parent forum
356 357 field_private_notes: Private notes
357 358 field_inherit_members: Inherit members
358 359 field_generate_password: Generate password
359 360 field_must_change_passwd: Must change password at next logon
360 361 field_default_status: Default status
361 362 field_users_visibility: Users visibility
362 363 field_time_entries_visibility: Time logs visibility
363 364 field_total_estimated_hours: Total estimated time
364 365 field_default_version: Default version
365 366 field_remote_ip: IP address
366 367
367 368 setting_app_title: Application title
368 369 setting_app_subtitle: Application subtitle
369 370 setting_welcome_text: Welcome text
370 371 setting_default_language: Default language
371 372 setting_login_required: Authentication required
372 373 setting_self_registration: Self-registration
373 374 setting_attachment_max_size: Maximum attachment size
374 375 setting_issues_export_limit: Issues export limit
375 376 setting_mail_from: Emission email address
376 377 setting_bcc_recipients: Blind carbon copy recipients (bcc)
377 378 setting_plain_text_mail: Plain text mail (no HTML)
378 379 setting_host_name: Host name and path
379 380 setting_text_formatting: Text formatting
380 381 setting_wiki_compression: Wiki history compression
381 382 setting_feeds_limit: Maximum number of items in Atom feeds
382 383 setting_default_projects_public: New projects are public by default
383 384 setting_autofetch_changesets: Fetch commits automatically
384 385 setting_sys_api_enabled: Enable WS for repository management
385 386 setting_commit_ref_keywords: Referencing keywords
386 387 setting_commit_fix_keywords: Fixing keywords
387 388 setting_autologin: Autologin
388 389 setting_date_format: Date format
389 390 setting_time_format: Time format
390 391 setting_cross_project_issue_relations: Allow cross-project issue relations
391 392 setting_cross_project_subtasks: Allow cross-project subtasks
392 393 setting_issue_list_default_columns: Default columns displayed on the issue list
393 394 setting_repositories_encodings: Attachments and repositories encodings
394 395 setting_emails_header: Email header
395 396 setting_emails_footer: Email footer
396 397 setting_protocol: Protocol
397 398 setting_per_page_options: Objects per page options
398 399 setting_user_format: Users display format
399 400 setting_activity_days_default: Days displayed on project activity
400 401 setting_display_subprojects_issues: Display subprojects issues on main projects by default
401 402 setting_enabled_scm: Enabled SCM
402 403 setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
403 404 setting_mail_handler_api_enabled: Enable WS for incoming emails
404 405 setting_mail_handler_api_key: Incoming email WS API key
405 406 setting_sys_api_key: Repository management WS API key
406 407 setting_sequential_project_identifiers: Generate sequential project identifiers
407 408 setting_gravatar_enabled: Use Gravatar user icons
408 409 setting_gravatar_default: Default Gravatar image
409 410 setting_diff_max_lines_displayed: Maximum number of diff lines displayed
410 411 setting_file_max_size_displayed: Maximum size of text files displayed inline
411 412 setting_repository_log_display_limit: Maximum number of revisions displayed on file log
412 413 setting_openid: Allow OpenID login and registration
413 414 setting_password_max_age: Require password change after
414 415 setting_password_min_length: Minimum password length
415 416 setting_lost_password: Allow password reset via email
416 417 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
417 418 setting_default_projects_modules: Default enabled modules for new projects
418 419 setting_issue_done_ratio: Calculate the issue done ratio with
419 420 setting_issue_done_ratio_issue_field: Use the issue field
420 421 setting_issue_done_ratio_issue_status: Use the issue status
421 422 setting_start_of_week: Start calendars on
422 423 setting_rest_api_enabled: Enable REST web service
423 424 setting_cache_formatted_text: Cache formatted text
424 425 setting_default_notification_option: Default notification option
425 426 setting_commit_logtime_enabled: Enable time logging
426 427 setting_commit_logtime_activity_id: Activity for logged time
427 428 setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
428 429 setting_issue_group_assignment: Allow issue assignment to groups
429 430 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
430 431 setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed
431 432 setting_unsubscribe: Allow users to delete their own account
432 433 setting_session_lifetime: Session maximum lifetime
433 434 setting_session_timeout: Session inactivity timeout
434 435 setting_thumbnails_enabled: Display attachment thumbnails
435 436 setting_thumbnails_size: Thumbnails size (in pixels)
436 437 setting_non_working_week_days: Non-working days
437 438 setting_jsonp_enabled: Enable JSONP support
438 439 setting_default_projects_tracker_ids: Default trackers for new projects
439 440 setting_mail_handler_excluded_filenames: Exclude attachments by name
440 441 setting_force_default_language_for_anonymous: Force default language for anonymous users
441 442 setting_force_default_language_for_loggedin: Force default language for logged-in users
442 443 setting_link_copied_issue: Link issues on copy
443 444 setting_max_additional_emails: Maximum number of additional email addresses
444 445 setting_search_results_per_page: Search results per page
445 446 setting_attachment_extensions_allowed: Allowed extensions
446 447 setting_attachment_extensions_denied: Disallowed extensions
447 448 setting_new_item_menu_tab: Project menu tab for creating new objects
448 449
449 450 permission_add_project: Create project
450 451 permission_add_subprojects: Create subprojects
451 452 permission_edit_project: Edit project
452 453 permission_close_project: Close / reopen the project
453 454 permission_select_project_modules: Select project modules
454 455 permission_manage_members: Manage members
455 456 permission_manage_project_activities: Manage project activities
456 457 permission_manage_versions: Manage versions
457 458 permission_manage_categories: Manage issue categories
458 459 permission_view_issues: View Issues
459 460 permission_add_issues: Add issues
460 461 permission_edit_issues: Edit issues
461 462 permission_copy_issues: Copy issues
462 463 permission_manage_issue_relations: Manage issue relations
463 464 permission_set_issues_private: Set issues public or private
464 465 permission_set_own_issues_private: Set own issues public or private
465 466 permission_add_issue_notes: Add notes
466 467 permission_edit_issue_notes: Edit notes
467 468 permission_edit_own_issue_notes: Edit own notes
468 469 permission_view_private_notes: View private notes
469 470 permission_set_notes_private: Set notes as private
470 471 permission_move_issues: Move issues
471 472 permission_delete_issues: Delete issues
472 473 permission_manage_public_queries: Manage public queries
473 474 permission_save_queries: Save queries
474 475 permission_view_gantt: View gantt chart
475 476 permission_view_calendar: View calendar
476 477 permission_view_issue_watchers: View watchers list
477 478 permission_add_issue_watchers: Add watchers
478 479 permission_delete_issue_watchers: Delete watchers
479 480 permission_log_time: Log spent time
480 481 permission_view_time_entries: View spent time
481 482 permission_edit_time_entries: Edit time logs
482 483 permission_edit_own_time_entries: Edit own time logs
483 484 permission_manage_news: Manage news
484 485 permission_comment_news: Comment news
485 486 permission_view_documents: View documents
486 487 permission_add_documents: Add documents
487 488 permission_edit_documents: Edit documents
488 489 permission_delete_documents: Delete documents
489 490 permission_manage_files: Manage files
490 491 permission_view_files: View files
491 492 permission_manage_wiki: Manage wiki
492 493 permission_rename_wiki_pages: Rename wiki pages
493 494 permission_delete_wiki_pages: Delete wiki pages
494 495 permission_view_wiki_pages: View wiki
495 496 permission_view_wiki_edits: View wiki history
496 497 permission_edit_wiki_pages: Edit wiki pages
497 498 permission_delete_wiki_pages_attachments: Delete attachments
498 499 permission_protect_wiki_pages: Protect wiki pages
499 500 permission_manage_repository: Manage repository
500 501 permission_browse_repository: Browse repository
501 502 permission_view_changesets: View changesets
502 503 permission_commit_access: Commit access
503 504 permission_manage_boards: Manage forums
504 505 permission_view_messages: View messages
505 506 permission_add_messages: Post messages
506 507 permission_edit_messages: Edit messages
507 508 permission_edit_own_messages: Edit own messages
508 509 permission_delete_messages: Delete messages
509 510 permission_delete_own_messages: Delete own messages
510 511 permission_export_wiki_pages: Export wiki pages
511 512 permission_manage_subtasks: Manage subtasks
512 513 permission_manage_related_issues: Manage related issues
513 514 permission_import_issues: Import issues
514 515
515 516 project_module_issue_tracking: Issue tracking
516 517 project_module_time_tracking: Time tracking
517 518 project_module_news: News
518 519 project_module_documents: Documents
519 520 project_module_files: Files
520 521 project_module_wiki: Wiki
521 522 project_module_repository: Repository
522 523 project_module_boards: Forums
523 524 project_module_calendar: Calendar
524 525 project_module_gantt: Gantt
525 526
526 527 label_user: User
527 528 label_user_plural: Users
528 529 label_user_new: New user
529 530 label_user_anonymous: Anonymous
530 531 label_project: Project
531 532 label_project_new: New project
532 533 label_project_plural: Projects
533 534 label_x_projects:
534 535 zero: no projects
535 536 one: 1 project
536 537 other: "%{count} projects"
537 538 label_project_all: All Projects
538 539 label_project_latest: Latest projects
539 540 label_issue: Issue
540 541 label_issue_new: New issue
541 542 label_issue_plural: Issues
542 543 label_issue_view_all: View all issues
543 544 label_issues_by: "Issues by %{value}"
544 545 label_issue_added: Issue added
545 546 label_issue_updated: Issue updated
546 547 label_issue_note_added: Note added
547 548 label_issue_status_updated: Status updated
548 549 label_issue_assigned_to_updated: Assignee updated
549 550 label_issue_priority_updated: Priority updated
550 551 label_document: Document
551 552 label_document_new: New document
552 553 label_document_plural: Documents
553 554 label_document_added: Document added
554 555 label_role: Role
555 556 label_role_plural: Roles
556 557 label_role_new: New role
557 558 label_role_and_permissions: Roles and permissions
558 559 label_role_anonymous: Anonymous
559 560 label_role_non_member: Non member
560 561 label_member: Member
561 562 label_member_new: New member
562 563 label_member_plural: Members
563 564 label_tracker: Tracker
564 565 label_tracker_plural: Trackers
565 566 label_tracker_all: All trackers
566 567 label_tracker_new: New tracker
567 568 label_workflow: Workflow
568 569 label_issue_status: Issue status
569 570 label_issue_status_plural: Issue statuses
570 571 label_issue_status_new: New status
571 572 label_issue_category: Issue category
572 573 label_issue_category_plural: Issue categories
573 574 label_issue_category_new: New category
574 575 label_custom_field: Custom field
575 576 label_custom_field_plural: Custom fields
576 577 label_custom_field_new: New custom field
577 578 label_enumerations: Enumerations
578 579 label_enumeration_new: New value
579 580 label_information: Information
580 581 label_information_plural: Information
581 582 label_please_login: Please log in
582 583 label_register: Register
583 584 label_login_with_open_id_option: or login with OpenID
584 585 label_password_lost: Lost password
585 586 label_password_required: Confirm your password to continue
586 587 label_home: Home
587 588 label_my_page: My page
588 589 label_my_account: My account
589 590 label_my_projects: My projects
590 591 label_my_page_block: My page block
591 592 label_administration: Administration
592 593 label_login: Sign in
593 594 label_logout: Sign out
594 595 label_help: Help
595 596 label_reported_issues: Reported issues
596 597 label_assigned_issues: Assigned issues
597 598 label_assigned_to_me_issues: Issues assigned to me
598 599 label_last_login: Last connection
599 600 label_registered_on: Registered on
600 601 label_activity: Activity
601 602 label_overall_activity: Overall activity
602 603 label_user_activity: "%{value}'s activity"
603 604 label_new: New
604 605 label_logged_as: Logged in as
605 606 label_environment: Environment
606 607 label_authentication: Authentication
607 608 label_auth_source: Authentication mode
608 609 label_auth_source_new: New authentication mode
609 610 label_auth_source_plural: Authentication modes
610 611 label_subproject_plural: Subprojects
611 612 label_subproject_new: New subproject
612 613 label_and_its_subprojects: "%{value} and its subprojects"
613 614 label_min_max_length: Min - Max length
614 615 label_list: List
615 616 label_date: Date
616 617 label_integer: Integer
617 618 label_float: Float
618 619 label_boolean: Boolean
619 620 label_string: Text
620 621 label_text: Long text
621 622 label_attribute: Attribute
622 623 label_attribute_plural: Attributes
623 624 label_no_data: No data to display
624 625 label_no_preview: No preview available
625 626 label_change_status: Change status
626 627 label_history: History
627 628 label_attachment: File
628 629 label_attachment_new: New file
629 630 label_attachment_delete: Delete file
630 631 label_attachment_plural: Files
631 632 label_file_added: File added
632 633 label_report: Report
633 634 label_report_plural: Reports
634 635 label_news: News
635 636 label_news_new: Add news
636 637 label_news_plural: News
637 638 label_news_latest: Latest news
638 639 label_news_view_all: View all news
639 640 label_news_added: News added
640 641 label_news_comment_added: Comment added to a news
641 642 label_settings: Settings
642 643 label_overview: Overview
643 644 label_version: Version
644 645 label_version_new: New version
645 646 label_version_plural: Versions
646 647 label_close_versions: Close completed versions
647 648 label_confirmation: Confirmation
648 649 label_export_to: 'Also available in:'
649 650 label_read: Read...
650 651 label_public_projects: Public projects
651 652 label_open_issues: open
652 653 label_open_issues_plural: open
653 654 label_closed_issues: closed
654 655 label_closed_issues_plural: closed
655 656 label_x_open_issues_abbr:
656 657 zero: 0 open
657 658 one: 1 open
658 659 other: "%{count} open"
659 660 label_x_closed_issues_abbr:
660 661 zero: 0 closed
661 662 one: 1 closed
662 663 other: "%{count} closed"
663 664 label_x_issues:
664 665 zero: 0 issues
665 666 one: 1 issue
666 667 other: "%{count} issues"
667 668 label_total: Total
668 669 label_total_plural: Totals
669 670 label_total_time: Total time
670 671 label_permissions: Permissions
671 672 label_current_status: Current status
672 673 label_new_statuses_allowed: New statuses allowed
673 674 label_all: all
674 675 label_any: any
675 676 label_none: none
676 677 label_nobody: nobody
677 678 label_next: Next
678 679 label_previous: Previous
679 680 label_used_by: Used by
680 681 label_details: Details
681 682 label_add_note: Add a note
682 683 label_calendar: Calendar
683 684 label_months_from: months from
684 685 label_gantt: Gantt
685 686 label_internal: Internal
686 687 label_last_changes: "last %{count} changes"
687 688 label_change_view_all: View all changes
688 689 label_personalize_page: Personalize this page
689 690 label_comment: Comment
690 691 label_comment_plural: Comments
691 692 label_x_comments:
692 693 zero: no comments
693 694 one: 1 comment
694 695 other: "%{count} comments"
695 696 label_comment_add: Add a comment
696 697 label_comment_added: Comment added
697 698 label_comment_delete: Delete comments
698 699 label_query: Custom query
699 700 label_query_plural: Custom queries
700 701 label_query_new: New query
701 702 label_my_queries: My custom queries
702 703 label_filter_add: Add filter
703 704 label_filter_plural: Filters
704 705 label_equals: is
705 706 label_not_equals: is not
706 707 label_in_less_than: in less than
707 708 label_in_more_than: in more than
708 709 label_in_the_next_days: in the next
709 710 label_in_the_past_days: in the past
710 711 label_greater_or_equal: '>='
711 712 label_less_or_equal: '<='
712 713 label_between: between
713 714 label_in: in
714 715 label_today: today
715 716 label_all_time: all time
716 717 label_yesterday: yesterday
717 718 label_this_week: this week
718 719 label_last_week: last week
719 720 label_last_n_weeks: "last %{count} weeks"
720 721 label_last_n_days: "last %{count} days"
721 722 label_this_month: this month
722 723 label_last_month: last month
723 724 label_this_year: this year
724 725 label_date_range: Date range
725 726 label_less_than_ago: less than days ago
726 727 label_more_than_ago: more than days ago
727 728 label_ago: days ago
728 729 label_contains: contains
729 730 label_not_contains: doesn't contain
730 731 label_any_issues_in_project: any issues in project
731 732 label_any_issues_not_in_project: any issues not in project
732 733 label_no_issues_in_project: no issues in project
733 734 label_any_open_issues: any open issues
734 735 label_no_open_issues: no open issues
735 736 label_day_plural: days
736 737 label_repository: Repository
737 738 label_repository_new: New repository
738 739 label_repository_plural: Repositories
739 740 label_browse: Browse
740 741 label_branch: Branch
741 742 label_tag: Tag
742 743 label_revision: Revision
743 744 label_revision_plural: Revisions
744 745 label_revision_id: "Revision %{value}"
745 746 label_associated_revisions: Associated revisions
746 747 label_added: added
747 748 label_modified: modified
748 749 label_copied: copied
749 750 label_renamed: renamed
750 751 label_deleted: deleted
751 752 label_latest_revision: Latest revision
752 753 label_latest_revision_plural: Latest revisions
753 754 label_view_revisions: View revisions
754 755 label_view_all_revisions: View all revisions
755 756 label_max_size: Maximum size
756 757 label_sort_highest: Move to top
757 758 label_sort_higher: Move up
758 759 label_sort_lower: Move down
759 760 label_sort_lowest: Move to bottom
760 761 label_roadmap: Roadmap
761 762 label_roadmap_due_in: "Due in %{value}"
762 763 label_roadmap_overdue: "%{value} late"
763 764 label_roadmap_no_issues: No issues for this version
764 765 label_search: Search
765 766 label_result_plural: Results
766 767 label_all_words: All words
767 768 label_wiki: Wiki
768 769 label_wiki_edit: Wiki edit
769 770 label_wiki_edit_plural: Wiki edits
770 771 label_wiki_page: Wiki page
771 772 label_wiki_page_plural: Wiki pages
772 773 label_wiki_page_new: New wiki page
773 774 label_index_by_title: Index by title
774 775 label_index_by_date: Index by date
775 776 label_current_version: Current version
776 777 label_preview: Preview
777 778 label_feed_plural: Feeds
778 779 label_changes_details: Details of all changes
779 780 label_issue_tracking: Issue tracking
780 781 label_spent_time: Spent time
781 782 label_total_spent_time: Total spent time
782 783 label_overall_spent_time: Overall spent time
783 784 label_f_hour: "%{value} hour"
784 785 label_f_hour_plural: "%{value} hours"
785 786 label_f_hour_short: "%{value} h"
786 787 label_time_tracking: Time tracking
787 788 label_change_plural: Changes
788 789 label_statistics: Statistics
789 790 label_commits_per_month: Commits per month
790 791 label_commits_per_author: Commits per author
791 792 label_diff: diff
792 793 label_view_diff: View differences
793 794 label_diff_inline: inline
794 795 label_diff_side_by_side: side by side
795 796 label_options: Options
796 797 label_copy_workflow_from: Copy workflow from
797 798 label_permissions_report: Permissions report
798 799 label_watched_issues: Watched issues
799 800 label_related_issues: Related issues
800 801 label_applied_status: Applied status
801 802 label_loading: Loading...
802 803 label_relation_new: New relation
803 804 label_relation_delete: Delete relation
804 805 label_relates_to: Related to
805 806 label_duplicates: Duplicates
806 807 label_duplicated_by: Duplicated by
807 808 label_blocks: Blocks
808 809 label_blocked_by: Blocked by
809 810 label_precedes: Precedes
810 811 label_follows: Follows
811 812 label_copied_to: Copied to
812 813 label_copied_from: Copied from
813 814 label_stay_logged_in: Stay logged in
814 815 label_disabled: disabled
815 816 label_show_completed_versions: Show completed versions
816 817 label_me: me
817 818 label_board: Forum
818 819 label_board_new: New forum
819 820 label_board_plural: Forums
820 821 label_board_locked: Locked
821 822 label_board_sticky: Sticky
822 823 label_topic_plural: Topics
823 824 label_message_plural: Messages
824 825 label_message_last: Last message
825 826 label_message_new: New message
826 827 label_message_posted: Message added
827 828 label_reply_plural: Replies
828 829 label_send_information: Send account information to the user
829 830 label_year: Year
830 831 label_month: Month
831 832 label_week: Week
832 833 label_date_from: From
833 834 label_date_to: To
834 835 label_language_based: Based on user's language
835 836 label_sort_by: "Sort by %{value}"
836 837 label_send_test_email: Send a test email
837 838 label_feeds_access_key: Atom access key
838 839 label_missing_feeds_access_key: Missing a Atom access key
839 840 label_feeds_access_key_created_on: "Atom access key created %{value} ago"
840 841 label_module_plural: Modules
841 842 label_added_time_by: "Added by %{author} %{age} ago"
842 843 label_updated_time_by: "Updated by %{author} %{age} ago"
843 844 label_updated_time: "Updated %{value} ago"
844 845 label_jump_to_a_project: Jump to a project...
845 846 label_file_plural: Files
846 847 label_changeset_plural: Changesets
847 848 label_default_columns: Default columns
848 849 label_no_change_option: (No change)
849 850 label_bulk_edit_selected_issues: Bulk edit selected issues
850 851 label_bulk_edit_selected_time_entries: Bulk edit selected time entries
851 852 label_theme: Theme
852 853 label_default: Default
853 854 label_search_titles_only: Search titles only
854 855 label_user_mail_option_all: "For any event on all my projects"
855 856 label_user_mail_option_selected: "For any event on the selected projects only..."
856 857 label_user_mail_option_none: "No events"
857 858 label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in"
858 859 label_user_mail_option_only_assigned: "Only for things I am assigned to"
859 860 label_user_mail_option_only_owner: "Only for things I am the owner of"
860 861 label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself"
861 862 label_registration_activation_by_email: account activation by email
862 863 label_registration_manual_activation: manual account activation
863 864 label_registration_automatic_activation: automatic account activation
864 865 label_display_per_page: "Per page: %{value}"
865 866 label_age: Age
866 867 label_change_properties: Change properties
867 868 label_general: General
868 869 label_more: More
869 870 label_scm: SCM
870 871 label_plugins: Plugins
871 872 label_ldap_authentication: LDAP authentication
872 873 label_downloads_abbr: D/L
873 874 label_optional_description: Optional description
874 875 label_add_another_file: Add another file
875 876 label_preferences: Preferences
876 877 label_chronological_order: In chronological order
877 878 label_reverse_chronological_order: In reverse chronological order
878 879 label_planning: Planning
879 880 label_incoming_emails: Incoming emails
880 881 label_generate_key: Generate a key
881 882 label_issue_watchers: Watchers
882 883 label_example: Example
883 884 label_display: Display
884 885 label_sort: Sort
885 886 label_ascending: Ascending
886 887 label_descending: Descending
887 888 label_date_from_to: From %{start} to %{end}
888 889 label_wiki_content_added: Wiki page added
889 890 label_wiki_content_updated: Wiki page updated
890 891 label_group: Group
891 892 label_group_plural: Groups
892 893 label_group_new: New group
893 894 label_group_anonymous: Anonymous users
894 895 label_group_non_member: Non member users
895 896 label_time_entry_plural: Spent time
896 897 label_version_sharing_none: Not shared
897 898 label_version_sharing_descendants: With subprojects
898 899 label_version_sharing_hierarchy: With project hierarchy
899 900 label_version_sharing_tree: With project tree
900 901 label_version_sharing_system: With all projects
901 902 label_update_issue_done_ratios: Update issue done ratios
902 903 label_copy_source: Source
903 904 label_copy_target: Target
904 905 label_copy_same_as_target: Same as target
905 906 label_display_used_statuses_only: Only display statuses that are used by this tracker
906 907 label_api_access_key: API access key
907 908 label_missing_api_access_key: Missing an API access key
908 909 label_api_access_key_created_on: "API access key created %{value} ago"
909 910 label_profile: Profile
910 911 label_subtask_plural: Subtasks
911 912 label_project_copy_notifications: Send email notifications during the project copy
912 913 label_principal_search: "Search for user or group:"
913 914 label_user_search: "Search for user:"
914 915 label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author
915 916 label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee
916 917 label_issues_visibility_all: All issues
917 918 label_issues_visibility_public: All non private issues
918 919 label_issues_visibility_own: Issues created by or assigned to the user
919 920 label_git_report_last_commit: Report last commit for files and directories
920 921 label_parent_revision: Parent
921 922 label_child_revision: Child
922 923 label_export_options: "%{export_format} export options"
923 924 label_copy_attachments: Copy attachments
924 925 label_copy_subtasks: Copy subtasks
925 926 label_item_position: "%{position} of %{count}"
926 927 label_completed_versions: Completed versions
927 928 label_search_for_watchers: Search for watchers to add
928 929 label_session_expiration: Session expiration
929 930 label_show_closed_projects: View closed projects
930 931 label_status_transitions: Status transitions
931 932 label_fields_permissions: Fields permissions
932 933 label_readonly: Read-only
933 934 label_required: Required
934 935 label_hidden: Hidden
935 936 label_attribute_of_project: "Project's %{name}"
936 937 label_attribute_of_issue: "Issue's %{name}"
937 938 label_attribute_of_author: "Author's %{name}"
938 939 label_attribute_of_assigned_to: "Assignee's %{name}"
939 940 label_attribute_of_user: "User's %{name}"
940 941 label_attribute_of_fixed_version: "Target version's %{name}"
941 942 label_cross_project_descendants: With subprojects
942 943 label_cross_project_tree: With project tree
943 944 label_cross_project_hierarchy: With project hierarchy
944 945 label_cross_project_system: With all projects
945 946 label_gantt_progress_line: Progress line
946 947 label_visibility_private: to me only
947 948 label_visibility_roles: to these roles only
948 949 label_visibility_public: to any users
949 950 label_link: Link
950 951 label_only: only
951 952 label_drop_down_list: drop-down list
952 953 label_checkboxes: checkboxes
953 954 label_radio_buttons: radio buttons
954 955 label_link_values_to: Link values to URL
955 956 label_custom_field_select_type: Select the type of object to which the custom field is to be attached
956 957 label_check_for_updates: Check for updates
957 958 label_latest_compatible_version: Latest compatible version
958 959 label_unknown_plugin: Unknown plugin
959 960 label_add_projects: Add projects
960 961 label_users_visibility_all: All active users
961 962 label_users_visibility_members_of_visible_projects: Members of visible projects
962 963 label_edit_attachments: Edit attached files
963 964 label_link_copied_issue: Link copied issue
964 965 label_ask: Ask
965 966 label_search_attachments_yes: Search attachment filenames and descriptions
966 967 label_search_attachments_no: Do not search attachments
967 968 label_search_attachments_only: Search attachments only
968 969 label_search_open_issues_only: Open issues only
969 970 label_email_address_plural: Emails
970 971 label_email_address_add: Add email address
971 972 label_enable_notifications: Enable notifications
972 973 label_disable_notifications: Disable notifications
973 974 label_blank_value: blank
974 975 label_parent_task_attributes: Parent tasks attributes
975 976 label_parent_task_attributes_derived: Calculated from subtasks
976 977 label_parent_task_attributes_independent: Independent of subtasks
977 978 label_time_entries_visibility_all: All time entries
978 979 label_time_entries_visibility_own: Time entries created by the user
979 980 label_member_management: Member management
980 981 label_member_management_all_roles: All roles
981 982 label_member_management_selected_roles_only: Only these roles
982 983 label_import_issues: Import issues
983 984 label_select_file_to_import: Select the file to import
984 985 label_fields_separator: Field separator
985 986 label_fields_wrapper: Field wrapper
986 987 label_encoding: Encoding
987 988 label_comma_char: Comma
988 989 label_semi_colon_char: Semicolon
989 990 label_quote_char: Quote
990 991 label_double_quote_char: Double quote
991 992 label_fields_mapping: Fields mapping
992 993 label_file_content_preview: File content preview
993 994 label_create_missing_values: Create missing values
994 995 label_api: API
995 996 label_field_format_enumeration: Key/value list
996 997 label_default_values_for_new_users: Default values for new users
997 998 label_relations: Relations
998 999 label_new_project_issue_tab_enabled: Display the "New issue" tab
999 1000 label_new_object_tab_enabled: Display the "+" drop-down
1000 1001
1001 1002 button_login: Login
1002 1003 button_submit: Submit
1003 1004 button_save: Save
1004 1005 button_check_all: Check all
1005 1006 button_uncheck_all: Uncheck all
1006 1007 button_collapse_all: Collapse all
1007 1008 button_expand_all: Expand all
1008 1009 button_delete: Delete
1009 1010 button_create: Create
1010 1011 button_create_and_continue: Create and continue
1011 1012 button_test: Test
1012 1013 button_edit: Edit
1013 1014 button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
1014 1015 button_add: Add
1015 1016 button_change: Change
1016 1017 button_apply: Apply
1017 1018 button_clear: Clear
1018 1019 button_lock: Lock
1019 1020 button_unlock: Unlock
1020 1021 button_download: Download
1021 1022 button_list: List
1022 1023 button_view: View
1023 1024 button_move: Move
1024 1025 button_move_and_follow: Move and follow
1025 1026 button_back: Back
1026 1027 button_cancel: Cancel
1027 1028 button_activate: Activate
1028 1029 button_sort: Sort
1029 1030 button_log_time: Log time
1030 1031 button_rollback: Rollback to this version
1031 1032 button_watch: Watch
1032 1033 button_unwatch: Unwatch
1033 1034 button_reply: Reply
1034 1035 button_archive: Archive
1035 1036 button_unarchive: Unarchive
1036 1037 button_reset: Reset
1037 1038 button_rename: Rename
1038 1039 button_change_password: Change password
1039 1040 button_copy: Copy
1040 1041 button_copy_and_follow: Copy and follow
1041 1042 button_annotate: Annotate
1042 1043 button_update: Update
1043 1044 button_configure: Configure
1044 1045 button_quote: Quote
1045 1046 button_duplicate: Duplicate
1046 1047 button_show: Show
1047 1048 button_hide: Hide
1048 1049 button_edit_section: Edit this section
1049 1050 button_export: Export
1050 1051 button_delete_my_account: Delete my account
1051 1052 button_close: Close
1052 1053 button_reopen: Reopen
1053 1054 button_import: Import
1054 1055 button_filter: Filter
1055 1056
1056 1057 status_active: active
1057 1058 status_registered: registered
1058 1059 status_locked: locked
1059 1060
1060 1061 project_status_active: active
1061 1062 project_status_closed: closed
1062 1063 project_status_archived: archived
1063 1064
1064 1065 version_status_open: open
1065 1066 version_status_locked: locked
1066 1067 version_status_closed: closed
1067 1068
1068 1069 field_active: Active
1069 1070
1070 1071 text_select_mail_notifications: Select actions for which email notifications should be sent.
1071 1072 text_regexp_info: eg. ^[A-Z0-9]+$
1072 1073 text_min_max_length_info: 0 means no restriction
1073 1074 text_project_destroy_confirmation: Are you sure you want to delete this project and related data?
1074 1075 text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted."
1075 1076 text_workflow_edit: Select a role and a tracker to edit the workflow
1076 1077 text_are_you_sure: Are you sure?
1077 1078 text_journal_changed: "%{label} changed from %{old} to %{new}"
1078 1079 text_journal_changed_no_detail: "%{label} updated"
1079 1080 text_journal_set_to: "%{label} set to %{value}"
1080 1081 text_journal_deleted: "%{label} deleted (%{old})"
1081 1082 text_journal_added: "%{label} %{value} added"
1082 1083 text_tip_issue_begin_day: issue beginning this day
1083 1084 text_tip_issue_end_day: issue ending this day
1084 1085 text_tip_issue_begin_end_day: issue beginning and ending this day
1085 1086 text_project_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed, must start with a lower case letter.<br />Once saved, the identifier cannot be changed.'
1086 1087 text_caracters_maximum: "%{count} characters maximum."
1087 1088 text_caracters_minimum: "Must be at least %{count} characters long."
1088 1089 text_length_between: "Length between %{min} and %{max} characters."
1089 1090 text_tracker_no_workflow: No workflow defined for this tracker
1090 1091 text_unallowed_characters: Unallowed characters
1091 1092 text_comma_separated: Multiple values allowed (comma separated).
1092 1093 text_line_separated: Multiple values allowed (one line for each value).
1093 1094 text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
1094 1095 text_issue_added: "Issue %{id} has been reported by %{author}."
1095 1096 text_issue_updated: "Issue %{id} has been updated by %{author}."
1096 1097 text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content?
1097 1098 text_issue_category_destroy_question: "Some issues (%{count}) are assigned to this category. What do you want to do?"
1098 1099 text_issue_category_destroy_assignments: Remove category assignments
1099 1100 text_issue_category_reassign_to: Reassign issues to this category
1100 1101 text_user_mail_option: "For unselected projects, you will only receive notifications about things you watch or you're involved in (eg. issues you're the author or assignee)."
1101 1102 text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
1102 1103 text_load_default_configuration: Load the default configuration
1103 1104 text_status_changed_by_changeset: "Applied in changeset %{value}."
1104 1105 text_time_logged_by_changeset: "Applied in changeset %{value}."
1105 1106 text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s)?'
1106 1107 text_issues_destroy_descendants_confirmation: "This will also delete %{count} subtask(s)."
1107 1108 text_time_entries_destroy_confirmation: 'Are you sure you want to delete the selected time entr(y/ies)?'
1108 1109 text_select_project_modules: 'Select modules to enable for this project:'
1109 1110 text_default_administrator_account_changed: Default administrator account changed
1110 1111 text_file_repository_writable: Attachments directory writable
1111 1112 text_plugin_assets_writable: Plugin assets directory writable
1112 1113 text_rmagick_available: RMagick available (optional)
1113 1114 text_convert_available: ImageMagick convert available (optional)
1114 1115 text_destroy_time_entries_question: "%{hours} hours were reported on the issues you are about to delete. What do you want to do?"
1115 1116 text_destroy_time_entries: Delete reported hours
1116 1117 text_assign_time_entries_to_project: Assign reported hours to the project
1117 1118 text_reassign_time_entries: 'Reassign reported hours to this issue:'
1118 1119 text_user_wrote: "%{value} wrote:"
1119 1120 text_enumeration_destroy_question: "%{count} objects are assigned to the value β€œ%{name}”."
1120 1121 text_enumeration_category_reassign_to: 'Reassign them to this value:'
1121 1122 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/configuration.yml and restart the application to enable them."
1122 1123 text_repository_usernames_mapping: "Select or update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
1123 1124 text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
1124 1125 text_custom_field_possible_values_info: 'One line for each value'
1125 1126 text_wiki_page_destroy_question: "This page has %{descendants} child page(s) and descendant(s). What do you want to do?"
1126 1127 text_wiki_page_nullify_children: "Keep child pages as root pages"
1127 1128 text_wiki_page_destroy_children: "Delete child pages and all their descendants"
1128 1129 text_wiki_page_reassign_children: "Reassign child pages to this parent page"
1129 1130 text_own_membership_delete_confirmation: "You are about to remove some or all of your permissions and may no longer be able to edit this project after that.\nAre you sure you want to continue?"
1130 1131 text_zoom_in: Zoom in
1131 1132 text_zoom_out: Zoom out
1132 1133 text_warn_on_leaving_unsaved: "The current page contains unsaved text that will be lost if you leave this page."
1133 1134 text_scm_path_encoding_note: "Default: UTF-8"
1134 1135 text_subversion_repository_note: "Examples: file:///, http://, https://, svn://, svn+[tunnelscheme]://"
1135 1136 text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo)
1136 1137 text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo)
1137 1138 text_scm_command: Command
1138 1139 text_scm_command_version: Version
1139 1140 text_scm_config: You can configure your SCM commands in config/configuration.yml. Please restart the application after editing it.
1140 1141 text_scm_command_not_available: SCM command is not available. Please check settings on the administration panel.
1141 1142 text_issue_conflict_resolution_overwrite: "Apply my changes anyway (previous notes will be kept but some changes may be overwritten)"
1142 1143 text_issue_conflict_resolution_add_notes: "Add my notes and discard my other changes"
1143 1144 text_issue_conflict_resolution_cancel: "Discard all my changes and redisplay %{link}"
1144 1145 text_account_destroy_confirmation: "Are you sure you want to proceed?\nYour account will be permanently deleted, with no way to reactivate it."
1145 1146 text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
1146 1147 text_project_closed: This project is closed and read-only.
1147 1148 text_turning_multiple_off: "If you disable multiple values, multiple values will be removed in order to preserve only one value per item."
1148 1149
1149 1150 default_role_manager: Manager
1150 1151 default_role_developer: Developer
1151 1152 default_role_reporter: Reporter
1152 1153 default_tracker_bug: Bug
1153 1154 default_tracker_feature: Feature
1154 1155 default_tracker_support: Support
1155 1156 default_issue_status_new: New
1156 1157 default_issue_status_in_progress: In Progress
1157 1158 default_issue_status_resolved: Resolved
1158 1159 default_issue_status_feedback: Feedback
1159 1160 default_issue_status_closed: Closed
1160 1161 default_issue_status_rejected: Rejected
1161 1162 default_doc_category_user: User documentation
1162 1163 default_doc_category_tech: Technical documentation
1163 1164 default_priority_low: Low
1164 1165 default_priority_normal: Normal
1165 1166 default_priority_high: High
1166 1167 default_priority_urgent: Urgent
1167 1168 default_priority_immediate: Immediate
1168 1169 default_activity_design: Design
1169 1170 default_activity_development: Development
1170 1171
1171 1172 enumeration_issue_priorities: Issue priorities
1172 1173 enumeration_doc_categories: Document categories
1173 1174 enumeration_activities: Activities (time tracking)
1174 1175 enumeration_system_activity: System Activity
1175 1176 description_filter: Filter
1176 1177 description_search: Searchfield
1177 1178 description_choose_project: Projects
1178 1179 description_project_scope: Search scope
1179 1180 description_notes: Notes
1180 1181 description_message_content: Message content
1181 1182 description_query_sort_criteria_attribute: Sort attribute
1182 1183 description_query_sort_criteria_direction: Sort direction
1183 1184 description_user_mail_notification: Mail notification settings
1184 1185 description_available_columns: Available Columns
1185 1186 description_selected_columns: Selected Columns
1186 1187 description_all_columns: All Columns
1187 1188 description_issue_category_reassign: Choose issue category
1188 1189 description_wiki_subpages_reassign: Choose new parent page
1189 1190 description_date_range_list: Choose range from list
1190 1191 description_date_range_interval: Choose range by selecting start and end date
1191 1192 description_date_from: Enter start date
1192 1193 description_date_to: Enter end date
1193 1194 text_repository_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.'
@@ -1,1213 +1,1214
1 1 # French translations for Ruby on Rails
2 2 # by Christian Lescuyer (christian@flyingcoders.com)
3 3 # contributor: Sebastien Grosjean - ZenCocoon.com
4 4 # contributor: Thibaut Cuvelier - Developpez.com
5 5
6 6 fr:
7 7 direction: ltr
8 8 date:
9 9 formats:
10 10 default: "%d/%m/%Y"
11 11 short: "%e %b"
12 12 long: "%e %B %Y"
13 13 long_ordinal: "%e %B %Y"
14 14 only_day: "%e"
15 15
16 16 day_names: [dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi]
17 17 abbr_day_names: [dim, lun, mar, mer, jeu, ven, sam]
18 18
19 19 # Don't forget the nil at the beginning; there's no such thing as a 0th month
20 20 month_names: [~, janvier, fΓ©vrier, mars, avril, mai, juin, juillet, aoΓ»t, septembre, octobre, novembre, dΓ©cembre]
21 21 abbr_month_names: [~, jan., fΓ©v., mar., avr., mai, juin, juil., aoΓ»t, sept., oct., nov., dΓ©c.]
22 22 # Used in date_select and datime_select.
23 23 order:
24 24 - :day
25 25 - :month
26 26 - :year
27 27
28 28 time:
29 29 formats:
30 30 default: "%d/%m/%Y %H:%M"
31 31 time: "%H:%M"
32 32 short: "%d %b %H:%M"
33 33 long: "%A %d %B %Y %H:%M:%S %Z"
34 34 long_ordinal: "%A %d %B %Y %H:%M:%S %Z"
35 35 only_second: "%S"
36 36 am: 'am'
37 37 pm: 'pm'
38 38
39 39 datetime:
40 40 distance_in_words:
41 41 half_a_minute: "30 secondes"
42 42 less_than_x_seconds:
43 43 zero: "moins d'une seconde"
44 44 one: "moins d'uneΒ seconde"
45 45 other: "moins de %{count}Β secondes"
46 46 x_seconds:
47 47 one: "1Β seconde"
48 48 other: "%{count}Β secondes"
49 49 less_than_x_minutes:
50 50 zero: "moins d'une minute"
51 51 one: "moins d'uneΒ minute"
52 52 other: "moins de %{count}Β minutes"
53 53 x_minutes:
54 54 one: "1Β minute"
55 55 other: "%{count}Β minutes"
56 56 about_x_hours:
57 57 one: "environ une heure"
58 58 other: "environ %{count}Β heures"
59 59 x_hours:
60 60 one: "une heure"
61 61 other: "%{count}Β heures"
62 62 x_days:
63 63 one: "unΒ jour"
64 64 other: "%{count}Β jours"
65 65 about_x_months:
66 66 one: "environ un mois"
67 67 other: "environ %{count}Β mois"
68 68 x_months:
69 69 one: "unΒ mois"
70 70 other: "%{count}Β mois"
71 71 about_x_years:
72 72 one: "environ un an"
73 73 other: "environ %{count}Β ans"
74 74 over_x_years:
75 75 one: "plus d'un an"
76 76 other: "plus de %{count}Β ans"
77 77 almost_x_years:
78 78 one: "presqu'un an"
79 79 other: "presque %{count} ans"
80 80 prompts:
81 81 year: "AnnΓ©e"
82 82 month: "Mois"
83 83 day: "Jour"
84 84 hour: "Heure"
85 85 minute: "Minute"
86 86 second: "Seconde"
87 87
88 88 number:
89 89 format:
90 90 precision: 3
91 91 separator: ','
92 92 delimiter: 'Β '
93 93 currency:
94 94 format:
95 95 unit: '€'
96 96 precision: 2
97 97 format: '%nΒ %u'
98 98 human:
99 99 format:
100 100 precision: 3
101 101 storage_units:
102 102 format: "%n %u"
103 103 units:
104 104 byte:
105 105 one: "octet"
106 106 other: "octets"
107 107 kb: "ko"
108 108 mb: "Mo"
109 109 gb: "Go"
110 110 tb: "To"
111 111
112 112 support:
113 113 array:
114 114 sentence_connector: 'et'
115 115 skip_last_comma: true
116 116 word_connector: ", "
117 117 two_words_connector: " et "
118 118 last_word_connector: " et "
119 119
120 120 activerecord:
121 121 errors:
122 122 template:
123 123 header:
124 124 one: "Impossible d'enregistrer %{model} : une erreur"
125 125 other: "Impossible d'enregistrer %{model} : %{count} erreurs."
126 126 body: "Veuillez vΓ©rifier les champs suivantsΒ :"
127 127 messages:
128 128 inclusion: "n'est pas inclus(e) dans la liste"
129 129 exclusion: "n'est pas disponible"
130 130 invalid: "n'est pas valide"
131 131 confirmation: "ne concorde pas avec la confirmation"
132 132 accepted: "doit Γͺtre acceptΓ©(e)"
133 133 empty: "doit Γͺtre renseignΓ©(e)"
134 134 blank: "doit Γͺtre renseignΓ©(e)"
135 135 too_long: "est trop long (pas plus de %{count} caractères)"
136 136 too_short: "est trop court (au moins %{count} caractères)"
137 137 wrong_length: "ne fait pas la bonne longueur (doit comporter %{count} caractères)"
138 138 taken: "est dΓ©jΓ  utilisΓ©"
139 139 not_a_number: "n'est pas un nombre"
140 140 not_a_date: "n'est pas une date valide"
141 141 greater_than: "doit Γͺtre supΓ©rieur Γ  %{count}"
142 142 greater_than_or_equal_to: "doit Γͺtre supΓ©rieur ou Γ©gal Γ  %{count}"
143 143 equal_to: "doit Γͺtre Γ©gal Γ  %{count}"
144 144 less_than: "doit Γͺtre infΓ©rieur Γ  %{count}"
145 145 less_than_or_equal_to: "doit Γͺtre infΓ©rieur ou Γ©gal Γ  %{count}"
146 146 odd: "doit Γͺtre impair"
147 147 even: "doit Γͺtre pair"
148 148 greater_than_start_date: "doit Γͺtre postΓ©rieure Γ  la date de dΓ©but"
149 149 not_same_project: "n'appartient pas au mΓͺme projet"
150 150 circular_dependency: "Cette relation crΓ©erait une dΓ©pendance circulaire"
151 151 cant_link_an_issue_with_a_descendant: "Une demande ne peut pas Γͺtre liΓ©e Γ  l'une de ses sous-tΓ’ches"
152 152 earlier_than_minimum_start_date: "ne peut pas Γͺtre antΓ©rieure au %{date} Γ  cause des demandes qui prΓ©cΓ¨dent"
153 153
154 154 actionview_instancetag_blank_option: Choisir
155 155
156 156 general_text_No: 'Non'
157 157 general_text_Yes: 'Oui'
158 158 general_text_no: 'non'
159 159 general_text_yes: 'oui'
160 160 general_lang_name: 'French (FranΓ§ais)'
161 161 general_csv_separator: ';'
162 162 general_csv_decimal_separator: ','
163 163 general_csv_encoding: ISO-8859-1
164 164 general_pdf_fontname: freesans
165 165 general_pdf_monospaced_fontname: freemono
166 166 general_first_day_of_week: '1'
167 167
168 168 notice_account_updated: Le compte a été mis à jour avec succès.
169 169 notice_account_invalid_credentials: Identifiant ou mot de passe invalide.
170 170 notice_account_password_updated: Mot de passe mis à jour avec succès.
171 171 notice_account_wrong_password: Mot de passe incorrect
172 172 notice_account_register_done: Un message contenant les instructions pour activer votre compte vous a Γ©tΓ© envoyΓ© Γ  l'adresse %{email}.
173 173 notice_account_unknown_email: Aucun compte ne correspond Γ  cette adresse.
174 174 notice_account_not_activated_yet: Vous n'avez pas encore activΓ© votre compte. Si vous voulez recevoir un nouveau message d'activation, veuillez <a href="%{url}">cliquer sur ce lien</a>.
175 175 notice_account_locked: Votre compte est verrouillΓ©.
176 176 notice_can_t_change_password: Ce compte utilise une authentification externe. Impossible de changer le mot de passe.
177 177 notice_account_lost_email_sent: Un message contenant les instructions pour choisir un nouveau mot de passe vous a Γ©tΓ© envoyΓ©.
178 178 notice_account_activated: Votre compte a Γ©tΓ© activΓ©. Vous pouvez Γ  prΓ©sent vous connecter.
179 179 notice_successful_create: Création effectuée avec succès.
180 180 notice_successful_update: Mise à jour effectuée avec succès.
181 181 notice_successful_delete: Suppression effectuée avec succès.
182 182 notice_successful_connection: Connexion rΓ©ussie.
183 183 notice_file_not_found: "La page Γ  laquelle vous souhaitez accΓ©der n'existe pas ou a Γ©tΓ© supprimΓ©e."
184 184 notice_locking_conflict: Les donnΓ©es ont Γ©tΓ© mises Γ  jour par un autre utilisateur. Mise Γ  jour impossible.
185 185 notice_not_authorized: "Vous n'Γͺtes pas autorisΓ© Γ  accΓ©der Γ  cette page."
186 186 notice_not_authorized_archived_project: Le projet auquel vous tentez d'accΓ©der a Γ©tΓ© archivΓ©.
187 187 notice_email_sent: "Un email a Γ©tΓ© envoyΓ© Γ  %{value}"
188 188 notice_email_error: "Erreur lors de l'envoi de l'email (%{value})"
189 189 notice_feeds_access_key_reseted: "Votre clé d'accès aux flux Atom a été réinitialisée."
190 190 notice_api_access_key_reseted: Votre clé d'accès API a été réinitialisée.
191 191 notice_failed_to_save_issues: "%{count} demande(s) sur les %{total} sΓ©lectionnΓ©es n'ont pas pu Γͺtre mise(s) Γ  jour : %{ids}."
192 192 notice_failed_to_save_time_entries: "%{count} temps passΓ©(s) sur les %{total} sΓ©lectionnΓ©s n'ont pas pu Γͺtre mis Γ  jour: %{ids}."
193 193 notice_failed_to_save_members: "Erreur lors de la sauvegarde des membres: %{errors}."
194 194 notice_no_issue_selected: "Aucune demande sΓ©lectionnΓ©e ! Cochez les demandes que vous voulez mettre Γ  jour."
195 195 notice_account_pending: "Votre compte a été créé et attend l'approbation de l'administrateur."
196 196 notice_default_data_loaded: Paramétrage par défaut chargé avec succès.
197 197 notice_unable_delete_version: Impossible de supprimer cette version.
198 198 notice_unable_delete_time_entry: Impossible de supprimer le temps passΓ©.
199 199 notice_issue_done_ratios_updated: L'avancement des demandes a Γ©tΓ© mis Γ  jour.
200 200 notice_gantt_chart_truncated: "Le diagramme a Γ©tΓ© tronquΓ© car il excΓ¨de le nombre maximal d'Γ©lΓ©ments pouvant Γͺtre affichΓ©s (%{max})"
201 201 notice_issue_successful_create: "Demande %{id} créée."
202 202 notice_issue_update_conflict: "La demande a Γ©tΓ© mise Γ  jour par un autre utilisateur pendant que vous la modifiez."
203 203 notice_account_deleted: "Votre compte a Γ©tΓ© dΓ©finitivement supprimΓ©."
204 204 notice_user_successful_create: "Utilisateur %{id} créé."
205 205 notice_new_password_must_be_different: Votre nouveau mot de passe doit Γͺtre diffΓ©rent de votre mot de passe actuel
206 206 notice_import_finished: "%{count} Γ©lΓ©ments ont Γ©tΓ© importΓ©(s)"
207 207 notice_import_finished_with_errors: "%{count} Γ©lΓ©ment(s) sur %{total} n'ont pas pu Γͺtre importΓ©(s)"
208 208
209 209 error_can_t_load_default_data: "Une erreur s'est produite lors du chargement du paramΓ©trage : %{value}"
210 210 error_scm_not_found: "L'entrΓ©e et/ou la rΓ©vision demandΓ©e n'existe pas dans le dΓ©pΓ΄t."
211 211 error_scm_command_failed: "Une erreur s'est produite lors de l'accès au dépôt : %{value}"
212 212 error_scm_annotate: "L'entrΓ©e n'existe pas ou ne peut pas Γͺtre annotΓ©e."
213 213 error_scm_annotate_big_text_file: Cette entrΓ©e ne peut pas Γͺtre annotΓ©e car elle excΓ¨de la taille maximale.
214 214 error_issue_not_found_in_project: "La demande n'existe pas ou n'appartient pas Γ  ce projet"
215 215 error_no_tracker_in_project: "Aucun tracker n'est associΓ© Γ  ce projet. VΓ©rifier la configuration du projet."
216 216 error_no_default_issue_status: "Aucun statut de demande n'est dΓ©fini par dΓ©faut. VΓ©rifier votre configuration (Administration -> Statuts de demandes)."
217 217 error_can_not_delete_custom_field: Impossible de supprimer le champ personnalisΓ©
218 218 error_can_not_delete_tracker: Ce tracker contient des demandes et ne peut pas Γͺtre supprimΓ©.
219 219 error_can_not_remove_role: Ce rΓ΄le est utilisΓ© et ne peut pas Γͺtre supprimΓ©.
220 220 error_can_not_reopen_issue_on_closed_version: 'Une demande assignΓ©e Γ  une version fermΓ©e ne peut pas Γͺtre rΓ©ouverte'
221 221 error_can_not_archive_project: "Ce projet ne peut pas Γͺtre archivΓ©"
222 222 error_issue_done_ratios_not_updated: L'avancement des demandes n'a pas pu Γͺtre mis Γ  jour.
223 223 error_workflow_copy_source: 'Veuillez sΓ©lectionner un tracker et/ou un rΓ΄le source'
224 224 error_workflow_copy_target: 'Veuillez sΓ©lectionner les trackers et rΓ΄les cibles'
225 225 error_unable_delete_issue_status: Impossible de supprimer le statut de demande
226 226 error_unable_to_connect: Connexion impossible (%{value})
227 227 error_attachment_too_big: Ce fichier ne peut pas Γͺtre attachΓ© car il excΓ¨de la taille maximale autorisΓ©e (%{max_size})
228 228 error_session_expired: "Votre session a expirΓ©. Veuillez vous reconnecter."
229 229 warning_attachments_not_saved: "%{count} fichier(s) n'ont pas pu Γͺtre sauvegardΓ©s."
230 230 error_password_expired: "Votre mot de passe a expirΓ© ou nΓ©cessite d'Γͺtre changΓ©."
231 231 error_invalid_file_encoding: "Le fichier n'est pas un fichier %{encoding} valide"
232 232 error_invalid_csv_file_or_settings: "Le fichier n'est pas un fichier CSV ou n'est pas conforme aux paramètres sélectionnés"
233 233 error_can_not_read_import_file: "Une erreur est survenue lors de la lecture du fichier Γ  importer"
234 234 error_attachment_extension_not_allowed: "L'extension %{extension} n'est pas autorisΓ©e"
235 235 error_ldap_bind_credentials: "Identifiant ou mot de passe LDAP incorrect"
236 236 error_no_tracker_allowed_for_new_issue_in_project: "Le projet ne dispose d'aucun tracker sur lequel vous pouvez crΓ©er une demande"
237 error_no_projects_with_tracker_allowed_for_new_issue: "Aucun projet ne dispose d'un tracker sur lequel vous pouvez crΓ©er une demande"
237 238
238 239 mail_subject_lost_password: "Votre mot de passe %{value}"
239 240 mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :'
240 241 mail_subject_register: "Activation de votre compte %{value}"
241 242 mail_body_register: 'Pour activer votre compte, cliquez sur le lien suivant :'
242 243 mail_body_account_information_external: "Vous pouvez utiliser votre compte %{value} pour vous connecter."
243 244 mail_body_account_information: Paramètres de connexion de votre compte
244 245 mail_subject_account_activation_request: "Demande d'activation d'un compte %{value}"
245 246 mail_body_account_activation_request: "Un nouvel utilisateur (%{value}) s'est inscrit. Son compte nΓ©cessite votre approbation :"
246 247 mail_subject_reminder: "%{count} demande(s) arrivent Γ  Γ©chΓ©ance (%{days})"
247 248 mail_body_reminder: "%{count} demande(s) qui vous sont assignΓ©es arrivent Γ  Γ©chΓ©ance dans les %{days} prochains jours :"
248 249 mail_subject_wiki_content_added: "Page wiki '%{id}' ajoutΓ©e"
249 250 mail_body_wiki_content_added: "La page wiki '%{id}' a Γ©tΓ© ajoutΓ©e par %{author}."
250 251 mail_subject_wiki_content_updated: "Page wiki '%{id}' mise Γ  jour"
251 252 mail_body_wiki_content_updated: "La page wiki '%{id}' a Γ©tΓ© mise Γ  jour par %{author}."
252 253 mail_body_settings_updated: "Les paramètres suivants ont été modifiés :"
253 254 mail_body_password_updated: "Votre mot de passe a Γ©tΓ© changΓ©."
254 255
255 256 field_name: Nom
256 257 field_description: Description
257 258 field_summary: RΓ©sumΓ©
258 259 field_is_required: Obligatoire
259 260 field_firstname: PrΓ©nom
260 261 field_lastname: Nom
261 262 field_mail: Email
262 263 field_address: Email
263 264 field_filename: Fichier
264 265 field_filesize: Taille
265 266 field_downloads: TΓ©lΓ©chargements
266 267 field_author: Auteur
267 268 field_created_on: Créé
268 269 field_updated_on: Mis-Γ -jour
269 270 field_closed_on: FermΓ©
270 271 field_field_format: Format
271 272 field_is_for_all: Pour tous les projets
272 273 field_possible_values: Valeurs possibles
273 274 field_regexp: Expression régulière
274 275 field_min_length: Longueur minimum
275 276 field_max_length: Longueur maximum
276 277 field_value: Valeur
277 278 field_category: CatΓ©gorie
278 279 field_title: Titre
279 280 field_project: Projet
280 281 field_issue: Demande
281 282 field_status: Statut
282 283 field_notes: Notes
283 284 field_is_closed: Demande fermΓ©e
284 285 field_is_default: Valeur par dΓ©faut
285 286 field_tracker: Tracker
286 287 field_subject: Sujet
287 288 field_due_date: EchΓ©ance
288 289 field_assigned_to: AssignΓ© Γ 
289 290 field_priority: PrioritΓ©
290 291 field_fixed_version: Version cible
291 292 field_user: Utilisateur
292 293 field_principal: Principal
293 294 field_role: RΓ΄le
294 295 field_homepage: Site web
295 296 field_is_public: Public
296 297 field_parent: Sous-projet de
297 298 field_is_in_roadmap: Demandes affichΓ©es dans la roadmap
298 299 field_login: Identifiant
299 300 field_mail_notification: Notifications par mail
300 301 field_admin: Administrateur
301 302 field_last_login_on: Dernière connexion
302 303 field_language: Langue
303 304 field_effective_date: Date
304 305 field_password: Mot de passe
305 306 field_new_password: Nouveau mot de passe
306 307 field_password_confirmation: Confirmation
307 308 field_version: Version
308 309 field_type: Type
309 310 field_host: HΓ΄te
310 311 field_port: Port
311 312 field_account: Compte
312 313 field_base_dn: Base DN
313 314 field_attr_login: Attribut Identifiant
314 315 field_attr_firstname: Attribut PrΓ©nom
315 316 field_attr_lastname: Attribut Nom
316 317 field_attr_mail: Attribut Email
317 318 field_onthefly: CrΓ©ation des utilisateurs Γ  la volΓ©e
318 319 field_start_date: DΓ©but
319 320 field_done_ratio: "% rΓ©alisΓ©"
320 321 field_auth_source: Mode d'authentification
321 322 field_hide_mail: Cacher mon adresse mail
322 323 field_comments: Commentaire
323 324 field_url: URL
324 325 field_start_page: Page de dΓ©marrage
325 326 field_subproject: Sous-projet
326 327 field_hours: Heures
327 328 field_activity: ActivitΓ©
328 329 field_spent_on: Date
329 330 field_identifier: Identifiant
330 331 field_is_filter: UtilisΓ© comme filtre
331 332 field_issue_to: Demande liΓ©e
332 333 field_delay: Retard
333 334 field_assignable: Demandes assignables Γ  ce rΓ΄le
334 335 field_redirect_existing_links: Rediriger les liens existants
335 336 field_estimated_hours: Temps estimΓ©
336 337 field_column_names: Colonnes
337 338 field_time_entries: Temps passΓ©
338 339 field_time_zone: Fuseau horaire
339 340 field_searchable: UtilisΓ© pour les recherches
340 341 field_default_value: Valeur par dΓ©faut
341 342 field_comments_sorting: Afficher les commentaires
342 343 field_parent_title: Page parent
343 344 field_editable: Modifiable
344 345 field_watcher: Observateur
345 346 field_identity_url: URL OpenID
346 347 field_content: Contenu
347 348 field_group_by: Grouper par
348 349 field_sharing: Partage
349 350 field_parent_issue: TΓ’che parente
350 351 field_member_of_group: Groupe de l'assignΓ©
351 352 field_assigned_to_role: RΓ΄le de l'assignΓ©
352 353 field_text: Champ texte
353 354 field_visible: Visible
354 355 field_warn_on_leaving_unsaved: "M'avertir lorsque je quitte une page contenant du texte non sauvegardΓ©"
355 356 field_issues_visibility: VisibilitΓ© des demandes
356 357 field_is_private: PrivΓ©e
357 358 field_commit_logs_encoding: Encodage des messages de commit
358 359 field_scm_path_encoding: Encodage des chemins
359 360 field_path_to_repository: Chemin du dΓ©pΓ΄t
360 361 field_root_directory: RΓ©pertoire racine
361 362 field_cvsroot: CVSROOT
362 363 field_cvs_module: Module
363 364 field_repository_is_default: DΓ©pΓ΄t principal
364 365 field_multiple: Valeurs multiples
365 366 field_auth_source_ldap_filter: Filtre LDAP
366 367 field_core_fields: Champs standards
367 368 field_timeout: "Timeout (en secondes)"
368 369 field_board_parent: Forum parent
369 370 field_private_notes: Notes privΓ©es
370 371 field_inherit_members: HΓ©riter les membres
371 372 field_generate_password: GΓ©nΓ©rer un mot de passe
372 373 field_must_change_passwd: Doit changer de mot de passe Γ  la prochaine connexion
373 374 field_default_status: Statut par dΓ©faut
374 375 field_users_visibility: VisibilitΓ© des utilisateurs
375 376 field_time_entries_visibility: VisibilitΓ© du temps passΓ©
376 377 field_total_estimated_hours: Temps estimΓ© total
377 378 field_default_version: Version par dΓ©faut
378 379
379 380 setting_app_title: Titre de l'application
380 381 setting_app_subtitle: Sous-titre de l'application
381 382 setting_welcome_text: Texte d'accueil
382 383 setting_default_language: Langue par dΓ©faut
383 384 setting_login_required: Authentification obligatoire
384 385 setting_self_registration: Inscription des nouveaux utilisateurs
385 386 setting_attachment_max_size: Taille maximale des fichiers
386 387 setting_issues_export_limit: Limite d'exportation des demandes
387 388 setting_mail_from: Adresse d'Γ©mission
388 389 setting_bcc_recipients: Destinataires en copie cachΓ©e (cci)
389 390 setting_plain_text_mail: Mail en texte brut (non HTML)
390 391 setting_host_name: Nom d'hΓ΄te et chemin
391 392 setting_text_formatting: Formatage du texte
392 393 setting_wiki_compression: Compression de l'historique des pages wiki
393 394 setting_feeds_limit: Nombre maximal d'Γ©lΓ©ments dans les flux Atom
394 395 setting_default_projects_public: DΓ©finir les nouveaux projets comme publics par dΓ©faut
395 396 setting_autofetch_changesets: RΓ©cupΓ©ration automatique des commits
396 397 setting_sys_api_enabled: Activer les WS pour la gestion des dΓ©pΓ΄ts
397 398 setting_commit_ref_keywords: Mots-clΓ©s de rΓ©fΓ©rencement
398 399 setting_commit_fix_keywords: Mots-clΓ©s de rΓ©solution
399 400 setting_autologin: DurΓ©e maximale de connexion automatique
400 401 setting_date_format: Format de date
401 402 setting_time_format: Format d'heure
402 403 setting_cross_project_issue_relations: Autoriser les relations entre demandes de diffΓ©rents projets
403 404 setting_cross_project_subtasks: Autoriser les sous-tΓ’ches dans des projets diffΓ©rents
404 405 setting_issue_list_default_columns: Colonnes affichΓ©es par dΓ©faut sur la liste des demandes
405 406 setting_repositories_encodings: Encodages des fichiers et des dΓ©pΓ΄ts
406 407 setting_emails_header: En-tΓͺte des emails
407 408 setting_emails_footer: Pied-de-page des emails
408 409 setting_protocol: Protocole
409 410 setting_per_page_options: Options d'objets affichΓ©s par page
410 411 setting_user_format: Format d'affichage des utilisateurs
411 412 setting_activity_days_default: Nombre de jours affichΓ©s sur l'activitΓ© des projets
412 413 setting_display_subprojects_issues: Afficher par dΓ©faut les demandes des sous-projets sur les projets principaux
413 414 setting_enabled_scm: SCM activΓ©s
414 415 setting_mail_handler_body_delimiters: "Tronquer les emails après l'une de ces lignes"
415 416 setting_mail_handler_api_enabled: "Activer le WS pour la rΓ©ception d'emails"
416 417 setting_mail_handler_api_key: ClΓ© de protection de l'API
417 418 setting_sequential_project_identifiers: GΓ©nΓ©rer des identifiants de projet sΓ©quentiels
418 419 setting_gravatar_enabled: Afficher les Gravatar des utilisateurs
419 420 setting_gravatar_default: Image Gravatar par dΓ©faut
420 421 setting_diff_max_lines_displayed: Nombre maximum de lignes de diff affichΓ©es
421 422 setting_file_max_size_displayed: Taille maximum des fichiers texte affichΓ©s en ligne
422 423 setting_repository_log_display_limit: "Nombre maximum de rΓ©visions affichΓ©es sur l'historique d'un fichier"
423 424 setting_openid: "Autoriser l'authentification et l'enregistrement OpenID"
424 425 setting_password_max_age: Expiration des mots de passe après
425 426 setting_password_min_length: Longueur minimum des mots de passe
426 427 setting_new_project_user_role_id: RΓ΄le donnΓ© Γ  un utilisateur non-administrateur qui crΓ©e un projet
427 428 setting_default_projects_modules: Modules activΓ©s par dΓ©faut pour les nouveaux projets
428 429 setting_issue_done_ratio: Calcul de l'avancement des demandes
429 430 setting_issue_done_ratio_issue_field: 'Utiliser le champ % effectuΓ©'
430 431 setting_issue_done_ratio_issue_status: Utiliser le statut
431 432 setting_start_of_week: Jour de dΓ©but des calendriers
432 433 setting_rest_api_enabled: Activer l'API REST
433 434 setting_cache_formatted_text: Mettre en cache le texte formatΓ©
434 435 setting_default_notification_option: Option de notification par dΓ©faut
435 436 setting_commit_logtime_enabled: Permettre la saisie de temps
436 437 setting_commit_logtime_activity_id: ActivitΓ© pour le temps saisi
437 438 setting_gantt_items_limit: Nombre maximum d'Γ©lΓ©ments affichΓ©s sur le gantt
438 439 setting_issue_group_assignment: Permettre l'assignation des demandes aux groupes
439 440 setting_default_issue_start_date_to_creation_date: Donner Γ  la date de dΓ©but d'une nouvelle demande la valeur de la date du jour
440 441 setting_commit_cross_project_ref: Permettre le rΓ©fΓ©rencement et la rΓ©solution des demandes de tous les autres projets
441 442 setting_unsubscribe: Permettre aux utilisateurs de supprimer leur propre compte
442 443 setting_session_lifetime: DurΓ©e de vie maximale des sessions
443 444 setting_session_timeout: DurΓ©e maximale d'inactivitΓ©
444 445 setting_thumbnails_enabled: Afficher les vignettes des images
445 446 setting_thumbnails_size: Taille des vignettes (en pixels)
446 447 setting_non_working_week_days: Jours non travaillΓ©s
447 448 setting_jsonp_enabled: Activer le support JSONP
448 449 setting_default_projects_tracker_ids: Trackers par dΓ©faut pour les nouveaux projets
449 450 setting_mail_handler_excluded_filenames: Exclure les fichiers attachΓ©s par leur nom
450 451 setting_force_default_language_for_anonymous: Forcer la langue par dΓ©fault pour les utilisateurs anonymes
451 452 setting_force_default_language_for_loggedin: Forcer la langue par dΓ©fault pour les utilisateurs identifiΓ©s
452 453 setting_link_copied_issue: Lier les demandes lors de la copie
453 454 setting_max_additional_emails: Nombre maximal d'adresses email additionnelles
454 455 setting_search_results_per_page: RΓ©sultats de recherche affichΓ©s par page
455 456 setting_attachment_extensions_allowed: Extensions autorisΓ©es
456 457 setting_attachment_extensions_denied: Extensions non autorisΓ©es
457 458 setting_sys_api_key: ClΓ© de protection de l'API
458 459 setting_lost_password: Autoriser la rΓ©initialisation par email de mot de passe perdu
459 460 setting_new_item_menu_tab: Onglet de crΓ©ation d'objets dans le menu du project
460 461
461 462 permission_add_project: CrΓ©er un projet
462 463 permission_add_subprojects: CrΓ©er des sous-projets
463 464 permission_edit_project: Modifier le projet
464 465 permission_close_project: Fermer / rΓ©ouvrir le projet
465 466 permission_select_project_modules: Choisir les modules
466 467 permission_manage_members: GΓ©rer les membres
467 468 permission_manage_project_activities: GΓ©rer les activitΓ©s
468 469 permission_manage_versions: GΓ©rer les versions
469 470 permission_manage_categories: GΓ©rer les catΓ©gories de demandes
470 471 permission_view_issues: Voir les demandes
471 472 permission_add_issues: CrΓ©er des demandes
472 473 permission_edit_issues: Modifier les demandes
473 474 permission_copy_issues: Copier les demandes
474 475 permission_manage_issue_relations: GΓ©rer les relations
475 476 permission_set_issues_private: Rendre les demandes publiques ou privΓ©es
476 477 permission_set_own_issues_private: Rendre ses propres demandes publiques ou privΓ©es
477 478 permission_add_issue_notes: Ajouter des notes
478 479 permission_edit_issue_notes: Modifier les notes
479 480 permission_edit_own_issue_notes: Modifier ses propres notes
480 481 permission_view_private_notes: Voir les notes privΓ©es
481 482 permission_set_notes_private: Rendre les notes privΓ©es
482 483 permission_move_issues: DΓ©placer les demandes
483 484 permission_delete_issues: Supprimer les demandes
484 485 permission_manage_public_queries: GΓ©rer les requΓͺtes publiques
485 486 permission_save_queries: Sauvegarder les requΓͺtes
486 487 permission_view_gantt: Voir le gantt
487 488 permission_view_calendar: Voir le calendrier
488 489 permission_view_issue_watchers: Voir la liste des observateurs
489 490 permission_add_issue_watchers: Ajouter des observateurs
490 491 permission_delete_issue_watchers: Supprimer des observateurs
491 492 permission_log_time: Saisir le temps passΓ©
492 493 permission_view_time_entries: Voir le temps passΓ©
493 494 permission_edit_time_entries: Modifier les temps passΓ©s
494 495 permission_edit_own_time_entries: Modifier son propre temps passΓ©
495 496 permission_manage_news: GΓ©rer les annonces
496 497 permission_comment_news: Commenter les annonces
497 498 permission_view_documents: Voir les documents
498 499 permission_add_documents: Ajouter des documents
499 500 permission_edit_documents: Modifier les documents
500 501 permission_delete_documents: Supprimer les documents
501 502 permission_manage_files: GΓ©rer les fichiers
502 503 permission_view_files: Voir les fichiers
503 504 permission_manage_wiki: GΓ©rer le wiki
504 505 permission_rename_wiki_pages: Renommer les pages
505 506 permission_delete_wiki_pages: Supprimer les pages
506 507 permission_view_wiki_pages: Voir le wiki
507 508 permission_view_wiki_edits: "Voir l'historique des modifications"
508 509 permission_edit_wiki_pages: Modifier les pages
509 510 permission_delete_wiki_pages_attachments: Supprimer les fichiers joints
510 511 permission_protect_wiki_pages: ProtΓ©ger les pages
511 512 permission_manage_repository: GΓ©rer le dΓ©pΓ΄t de sources
512 513 permission_browse_repository: Parcourir les sources
513 514 permission_view_changesets: Voir les rΓ©visions
514 515 permission_commit_access: Droit de commit
515 516 permission_manage_boards: GΓ©rer les forums
516 517 permission_view_messages: Voir les messages
517 518 permission_add_messages: Poster un message
518 519 permission_edit_messages: Modifier les messages
519 520 permission_edit_own_messages: Modifier ses propres messages
520 521 permission_delete_messages: Supprimer les messages
521 522 permission_delete_own_messages: Supprimer ses propres messages
522 523 permission_export_wiki_pages: Exporter les pages
523 524 permission_manage_subtasks: GΓ©rer les sous-tΓ’ches
524 525 permission_manage_related_issues: GΓ©rer les demandes associΓ©es
525 526 permission_import_issues: Importer des demandes
526 527
527 528 project_module_issue_tracking: Suivi des demandes
528 529 project_module_time_tracking: Suivi du temps passΓ©
529 530 project_module_news: Publication d'annonces
530 531 project_module_documents: Publication de documents
531 532 project_module_files: Publication de fichiers
532 533 project_module_wiki: Wiki
533 534 project_module_repository: DΓ©pΓ΄t de sources
534 535 project_module_boards: Forums de discussion
535 536 project_module_calendar: Calendrier
536 537 project_module_gantt: Gantt
537 538
538 539 label_user: Utilisateur
539 540 label_user_plural: Utilisateurs
540 541 label_user_new: Nouvel utilisateur
541 542 label_user_anonymous: Anonyme
542 543 label_project: Projet
543 544 label_project_new: Nouveau projet
544 545 label_project_plural: Projets
545 546 label_x_projects:
546 547 zero: aucun projet
547 548 one: un projet
548 549 other: "%{count} projets"
549 550 label_project_all: Tous les projets
550 551 label_project_latest: Derniers projets
551 552 label_issue: Demande
552 553 label_issue_new: Nouvelle demande
553 554 label_issue_plural: Demandes
554 555 label_issue_view_all: Voir toutes les demandes
555 556 label_issues_by: "Demandes par %{value}"
556 557 label_issue_added: Demande ajoutΓ©e
557 558 label_issue_updated: Demande mise Γ  jour
558 559 label_issue_note_added: Note ajoutΓ©e
559 560 label_issue_status_updated: Statut changΓ©
560 561 label_issue_assigned_to_updated: AssignΓ© changΓ©
561 562 label_issue_priority_updated: PrioritΓ© changΓ©e
562 563 label_document: Document
563 564 label_document_new: Nouveau document
564 565 label_document_plural: Documents
565 566 label_document_added: Document ajoutΓ©
566 567 label_role: RΓ΄le
567 568 label_role_plural: RΓ΄les
568 569 label_role_new: Nouveau rΓ΄le
569 570 label_role_and_permissions: RΓ΄les et permissions
570 571 label_role_anonymous: Anonyme
571 572 label_role_non_member: Non membre
572 573 label_member: Membre
573 574 label_member_new: Nouveau membre
574 575 label_member_plural: Membres
575 576 label_tracker: Tracker
576 577 label_tracker_plural: Trackers
577 578 label_tracker_all: Tous les trackers
578 579 label_tracker_new: Nouveau tracker
579 580 label_workflow: Workflow
580 581 label_issue_status: Statut de demandes
581 582 label_issue_status_plural: Statuts de demandes
582 583 label_issue_status_new: Nouveau statut
583 584 label_issue_category: CatΓ©gorie de demandes
584 585 label_issue_category_plural: CatΓ©gories de demandes
585 586 label_issue_category_new: Nouvelle catΓ©gorie
586 587 label_custom_field: Champ personnalisΓ©
587 588 label_custom_field_plural: Champs personnalisΓ©s
588 589 label_custom_field_new: Nouveau champ personnalisΓ©
589 590 label_enumerations: Listes de valeurs
590 591 label_enumeration_new: Nouvelle valeur
591 592 label_information: Information
592 593 label_information_plural: Informations
593 594 label_please_login: Identification
594 595 label_register: S'enregistrer
595 596 label_login_with_open_id_option: S'authentifier avec OpenID
596 597 label_password_lost: Mot de passe perdu
597 598 label_password_required: Confirmez votre mot de passe pour continuer
598 599 label_home: Accueil
599 600 label_my_page: Ma page
600 601 label_my_account: Mon compte
601 602 label_my_projects: Mes projets
602 603 label_my_page_block: Blocs disponibles
603 604 label_administration: Administration
604 605 label_login: Connexion
605 606 label_logout: DΓ©connexion
606 607 label_help: Aide
607 608 label_reported_issues: Demandes soumises
608 609 label_assigned_issues: Demandes assignΓ©es
609 610 label_assigned_to_me_issues: Demandes qui me sont assignΓ©es
610 611 label_last_login: Dernière connexion
611 612 label_registered_on: Inscrit le
612 613 label_activity: ActivitΓ©
613 614 label_overall_activity: ActivitΓ© globale
614 615 label_user_activity: "ActivitΓ© de %{value}"
615 616 label_new: Nouveau
616 617 label_logged_as: ConnectΓ© en tant que
617 618 label_environment: Environnement
618 619 label_authentication: Authentification
619 620 label_auth_source: Mode d'authentification
620 621 label_auth_source_new: Nouveau mode d'authentification
621 622 label_auth_source_plural: Modes d'authentification
622 623 label_subproject_plural: Sous-projets
623 624 label_subproject_new: Nouveau sous-projet
624 625 label_and_its_subprojects: "%{value} et ses sous-projets"
625 626 label_min_max_length: Longueurs mini - maxi
626 627 label_list: Liste
627 628 label_date: Date
628 629 label_integer: Entier
629 630 label_float: Nombre dΓ©cimal
630 631 label_boolean: BoolΓ©en
631 632 label_string: Texte
632 633 label_text: Texte long
633 634 label_attribute: Attribut
634 635 label_attribute_plural: Attributs
635 636 label_no_data: Aucune donnΓ©e Γ  afficher
636 637 label_change_status: Changer le statut
637 638 label_history: Historique
638 639 label_attachment: Fichier
639 640 label_attachment_new: Nouveau fichier
640 641 label_attachment_delete: Supprimer le fichier
641 642 label_attachment_plural: Fichiers
642 643 label_file_added: Fichier ajoutΓ©
643 644 label_report: Rapport
644 645 label_report_plural: Rapports
645 646 label_news: Annonce
646 647 label_news_new: Nouvelle annonce
647 648 label_news_plural: Annonces
648 649 label_news_latest: Dernières annonces
649 650 label_news_view_all: Voir toutes les annonces
650 651 label_news_added: Annonce ajoutΓ©e
651 652 label_news_comment_added: Commentaire ajoutΓ© Γ  une annonce
652 653 label_settings: Configuration
653 654 label_overview: AperΓ§u
654 655 label_version: Version
655 656 label_version_new: Nouvelle version
656 657 label_version_plural: Versions
657 658 label_close_versions: Fermer les versions terminΓ©es
658 659 label_confirmation: Confirmation
659 660 label_export_to: 'Formats disponibles :'
660 661 label_read: Lire...
661 662 label_public_projects: Projets publics
662 663 label_open_issues: ouvert
663 664 label_open_issues_plural: ouverts
664 665 label_closed_issues: fermΓ©
665 666 label_closed_issues_plural: fermΓ©s
666 667 label_x_open_issues_abbr:
667 668 zero: 0 ouverte
668 669 one: 1 ouverte
669 670 other: "%{count} ouvertes"
670 671 label_x_closed_issues_abbr:
671 672 zero: 0 fermΓ©e
672 673 one: 1 fermΓ©e
673 674 other: "%{count} fermΓ©es"
674 675 label_x_issues:
675 676 zero: 0 demande
676 677 one: 1 demande
677 678 other: "%{count} demandes"
678 679 label_total: Total
679 680 label_total_plural: Totaux
680 681 label_total_time: Temps total
681 682 label_permissions: Permissions
682 683 label_current_status: Statut actuel
683 684 label_new_statuses_allowed: Nouveaux statuts autorisΓ©s
684 685 label_all: tous
685 686 label_any: tous
686 687 label_none: aucun
687 688 label_nobody: personne
688 689 label_next: Suivant
689 690 label_previous: PrΓ©cΓ©dent
690 691 label_used_by: UtilisΓ© par
691 692 label_details: DΓ©tails
692 693 label_add_note: Ajouter une note
693 694 label_calendar: Calendrier
694 695 label_months_from: mois depuis
695 696 label_gantt: Gantt
696 697 label_internal: Interne
697 698 label_last_changes: "%{count} derniers changements"
698 699 label_change_view_all: Voir tous les changements
699 700 label_personalize_page: Personnaliser cette page
700 701 label_comment: Commentaire
701 702 label_comment_plural: Commentaires
702 703 label_x_comments:
703 704 zero: aucun commentaire
704 705 one: un commentaire
705 706 other: "%{count} commentaires"
706 707 label_comment_add: Ajouter un commentaire
707 708 label_comment_added: Commentaire ajoutΓ©
708 709 label_comment_delete: Supprimer les commentaires
709 710 label_query: Rapport personnalisΓ©
710 711 label_query_plural: Rapports personnalisΓ©s
711 712 label_query_new: Nouveau rapport
712 713 label_my_queries: Mes rapports personnalisΓ©s
713 714 label_filter_add: Ajouter le filtre
714 715 label_filter_plural: Filtres
715 716 label_equals: Γ©gal
716 717 label_not_equals: diffΓ©rent
717 718 label_in_less_than: dans moins de
718 719 label_in_more_than: dans plus de
719 720 label_in_the_next_days: dans les prochains jours
720 721 label_in_the_past_days: dans les derniers jours
721 722 label_greater_or_equal: '>='
722 723 label_less_or_equal: '<='
723 724 label_between: entre
724 725 label_in: dans
725 726 label_today: aujourd'hui
726 727 label_all_time: toute la pΓ©riode
727 728 label_yesterday: hier
728 729 label_this_week: cette semaine
729 730 label_last_week: la semaine dernière
730 731 label_last_n_weeks: "les %{count} dernières semaines"
731 732 label_last_n_days: "les %{count} derniers jours"
732 733 label_this_month: ce mois-ci
733 734 label_last_month: le mois dernier
734 735 label_this_year: cette annΓ©e
735 736 label_date_range: PΓ©riode
736 737 label_less_than_ago: il y a moins de
737 738 label_more_than_ago: il y a plus de
738 739 label_ago: il y a
739 740 label_contains: contient
740 741 label_not_contains: ne contient pas
741 742 label_any_issues_in_project: une demande du projet
742 743 label_any_issues_not_in_project: une demande hors du projet
743 744 label_no_issues_in_project: aucune demande du projet
744 745 label_any_open_issues: une demande ouverte
745 746 label_no_open_issues: aucune demande ouverte
746 747 label_day_plural: jours
747 748 label_repository: DΓ©pΓ΄t
748 749 label_repository_new: Nouveau dΓ©pΓ΄t
749 750 label_repository_plural: DΓ©pΓ΄ts
750 751 label_browse: Parcourir
751 752 label_branch: Branche
752 753 label_tag: Tag
753 754 label_revision: RΓ©vision
754 755 label_revision_plural: RΓ©visions
755 756 label_revision_id: "RΓ©vision %{value}"
756 757 label_associated_revisions: RΓ©visions associΓ©es
757 758 label_added: ajoutΓ©
758 759 label_modified: modifiΓ©
759 760 label_copied: copiΓ©
760 761 label_renamed: renommΓ©
761 762 label_deleted: supprimΓ©
762 763 label_latest_revision: Dernière révision
763 764 label_latest_revision_plural: Dernières révisions
764 765 label_view_revisions: Voir les rΓ©visions
765 766 label_view_all_revisions: Voir toutes les rΓ©visions
766 767 label_max_size: Taille maximale
767 768 label_sort_highest: Remonter en premier
768 769 label_sort_higher: Remonter
769 770 label_sort_lower: Descendre
770 771 label_sort_lowest: Descendre en dernier
771 772 label_roadmap: Roadmap
772 773 label_roadmap_due_in: "Γ‰chΓ©ance dans %{value}"
773 774 label_roadmap_overdue: "En retard de %{value}"
774 775 label_roadmap_no_issues: Aucune demande pour cette version
775 776 label_search: Recherche
776 777 label_result_plural: RΓ©sultats
777 778 label_all_words: Tous les mots
778 779 label_wiki: Wiki
779 780 label_wiki_edit: RΓ©vision wiki
780 781 label_wiki_edit_plural: RΓ©visions wiki
781 782 label_wiki_page: Page wiki
782 783 label_wiki_page_plural: Pages wiki
783 784 label_wiki_page_new: Nouvelle page wiki
784 785 label_index_by_title: Index par titre
785 786 label_index_by_date: Index par date
786 787 label_current_version: Version actuelle
787 788 label_preview: PrΓ©visualisation
788 789 label_feed_plural: Flux Atom
789 790 label_changes_details: DΓ©tails de tous les changements
790 791 label_issue_tracking: Suivi des demandes
791 792 label_spent_time: Temps passΓ©
792 793 label_total_spent_time: Temps passΓ© total
793 794 label_overall_spent_time: Temps passΓ© global
794 795 label_f_hour: "%{value} heure"
795 796 label_f_hour_plural: "%{value} heures"
796 797 label_f_hour_short: "%{value} h"
797 798 label_time_tracking: Suivi du temps
798 799 label_change_plural: Changements
799 800 label_statistics: Statistiques
800 801 label_commits_per_month: Commits par mois
801 802 label_commits_per_author: Commits par auteur
802 803 label_diff: diff
803 804 label_view_diff: Voir les diffΓ©rences
804 805 label_diff_inline: en ligne
805 806 label_diff_side_by_side: cΓ΄te Γ  cΓ΄te
806 807 label_options: Options
807 808 label_copy_workflow_from: Copier le workflow de
808 809 label_permissions_report: Synthèse des permissions
809 810 label_watched_issues: Demandes surveillΓ©es
810 811 label_related_issues: Demandes liΓ©es
811 812 label_applied_status: Statut appliquΓ©
812 813 label_loading: Chargement...
813 814 label_relation_new: Nouvelle relation
814 815 label_relation_delete: Supprimer la relation
815 816 label_relates_to: LiΓ© Γ 
816 817 label_duplicates: Duplique
817 818 label_duplicated_by: DupliquΓ© par
818 819 label_blocks: Bloque
819 820 label_blocked_by: BloquΓ© par
820 821 label_precedes: Précède
821 822 label_follows: Suit
822 823 label_copied_to: CopiΓ© vers
823 824 label_copied_from: CopiΓ© depuis
824 825 label_stay_logged_in: Rester connectΓ©
825 826 label_disabled: dΓ©sactivΓ©
826 827 label_show_completed_versions: Voir les versions passΓ©es
827 828 label_me: moi
828 829 label_board: Forum
829 830 label_board_new: Nouveau forum
830 831 label_board_plural: Forums
831 832 label_board_locked: VerrouillΓ©
832 833 label_board_sticky: Sticky
833 834 label_topic_plural: Discussions
834 835 label_message_plural: Messages
835 836 label_message_last: Dernier message
836 837 label_message_new: Nouveau message
837 838 label_message_posted: Message ajoutΓ©
838 839 label_reply_plural: RΓ©ponses
839 840 label_send_information: Envoyer les informations Γ  l'utilisateur
840 841 label_year: AnnΓ©e
841 842 label_month: Mois
842 843 label_week: Semaine
843 844 label_date_from: Du
844 845 label_date_to: Au
845 846 label_language_based: BasΓ© sur la langue de l'utilisateur
846 847 label_sort_by: "Trier par %{value}"
847 848 label_send_test_email: Envoyer un email de test
848 849 label_feeds_access_key: Clé d'accès Atom
849 850 label_missing_feeds_access_key: Clé d'accès Atom manquante
850 851 label_feeds_access_key_created_on: "Clé d'accès Atom créée il y a %{value}"
851 852 label_module_plural: Modules
852 853 label_added_time_by: "AjoutΓ© par %{author} il y a %{age}"
853 854 label_updated_time_by: "Mis Γ  jour par %{author} il y a %{age}"
854 855 label_updated_time: "Mis Γ  jour il y a %{value}"
855 856 label_jump_to_a_project: Aller Γ  un projet...
856 857 label_file_plural: Fichiers
857 858 label_changeset_plural: RΓ©visions
858 859 label_default_columns: Colonnes par dΓ©faut
859 860 label_no_change_option: (Pas de changement)
860 861 label_bulk_edit_selected_issues: Modifier les demandes sΓ©lectionnΓ©es
861 862 label_bulk_edit_selected_time_entries: Modifier les temps passΓ©s sΓ©lectionnΓ©s
862 863 label_theme: Thème
863 864 label_default: DΓ©faut
864 865 label_search_titles_only: Uniquement dans les titres
865 866 label_user_mail_option_all: "Pour tous les Γ©vΓ©nements de tous mes projets"
866 867 label_user_mail_option_selected: "Pour tous les Γ©vΓ©nements des projets sΓ©lectionnΓ©s..."
867 868 label_user_mail_option_none: Aucune notification
868 869 label_user_mail_option_only_my_events: Seulement pour ce que je surveille
869 870 label_user_mail_option_only_assigned: Seulement pour ce qui m'est assignΓ©
870 871 label_user_mail_option_only_owner: Seulement pour ce que j'ai créé
871 872 label_user_mail_no_self_notified: "Je ne veux pas Γͺtre notifiΓ© des changements que j'effectue"
872 873 label_registration_activation_by_email: activation du compte par email
873 874 label_registration_manual_activation: activation manuelle du compte
874 875 label_registration_automatic_activation: activation automatique du compte
875 876 label_display_per_page: "Par page : %{value}"
876 877 label_age: Γ‚ge
877 878 label_change_properties: Changer les propriΓ©tΓ©s
878 879 label_general: GΓ©nΓ©ral
879 880 label_more: Plus
880 881 label_scm: SCM
881 882 label_plugins: Plugins
882 883 label_ldap_authentication: Authentification LDAP
883 884 label_downloads_abbr: D/L
884 885 label_optional_description: Description facultative
885 886 label_add_another_file: Ajouter un autre fichier
886 887 label_preferences: PrΓ©fΓ©rences
887 888 label_chronological_order: Dans l'ordre chronologique
888 889 label_reverse_chronological_order: Dans l'ordre chronologique inverse
889 890 label_planning: Planning
890 891 label_incoming_emails: Emails entrants
891 892 label_generate_key: GΓ©nΓ©rer une clΓ©
892 893 label_issue_watchers: Observateurs
893 894 label_example: Exemple
894 895 label_display: Affichage
895 896 label_sort: Tri
896 897 label_ascending: Croissant
897 898 label_descending: DΓ©croissant
898 899 label_date_from_to: Du %{start} au %{end}
899 900 label_wiki_content_added: Page wiki ajoutΓ©e
900 901 label_wiki_content_updated: Page wiki mise Γ  jour
901 902 label_group: Groupe
902 903 label_group_plural: Groupes
903 904 label_group_new: Nouveau groupe
904 905 label_group_anonymous: Utilisateurs anonymes
905 906 label_group_non_member: Utilisateurs non membres
906 907 label_time_entry_plural: Temps passΓ©
907 908 label_version_sharing_none: Non partagΓ©
908 909 label_version_sharing_descendants: Avec les sous-projets
909 910 label_version_sharing_hierarchy: Avec toute la hiΓ©rarchie
910 911 label_version_sharing_tree: Avec tout l'arbre
911 912 label_version_sharing_system: Avec tous les projets
912 913 label_update_issue_done_ratios: Mettre Γ  jour l'avancement des demandes
913 914 label_copy_source: Source
914 915 label_copy_target: Cible
915 916 label_copy_same_as_target: Comme la cible
916 917 label_display_used_statuses_only: N'afficher que les statuts utilisΓ©s dans ce tracker
917 918 label_api_access_key: Clé d'accès API
918 919 label_missing_api_access_key: Clé d'accès API manquante
919 920 label_api_access_key_created_on: Clé d'accès API créée il y a %{value}
920 921 label_profile: Profil
921 922 label_subtask_plural: Sous-tΓ’ches
922 923 label_project_copy_notifications: Envoyer les notifications durant la copie du projet
923 924 label_principal_search: "Rechercher un utilisateur ou un groupe :"
924 925 label_user_search: "Rechercher un utilisateur :"
925 926 label_additional_workflow_transitions_for_author: Autorisations supplémentaires lorsque l'utilisateur a créé la demande
926 927 label_additional_workflow_transitions_for_assignee: Autorisations supplΓ©mentaires lorsque la demande est assignΓ©e Γ  l'utilisateur
927 928 label_issues_visibility_all: Toutes les demandes
928 929 label_issues_visibility_public: Toutes les demandes non privΓ©es
929 930 label_issues_visibility_own: Demandes créées par ou assignées à l'utilisateur
930 931 label_git_report_last_commit: Afficher le dernier commit des fichiers et rΓ©pertoires
931 932 label_parent_revision: Parent
932 933 label_child_revision: Enfant
933 934 label_export_options: Options d'exportation %{export_format}
934 935 label_copy_attachments: Copier les fichiers
935 936 label_copy_subtasks: Copier les sous-tΓ’ches
936 937 label_item_position: "%{position} sur %{count}"
937 938 label_completed_versions: Versions passΓ©es
938 939 label_search_for_watchers: Rechercher des observateurs
939 940 label_session_expiration: Expiration des sessions
940 941 label_show_closed_projects: Voir les projets fermΓ©s
941 942 label_status_transitions: Changements de statut
942 943 label_fields_permissions: Permissions sur les champs
943 944 label_readonly: Lecture
944 945 label_required: Obligatoire
945 946 label_hidden: CachΓ©
946 947 label_attribute_of_project: "%{name} du projet"
947 948 label_attribute_of_issue: "%{name} de la demande"
948 949 label_attribute_of_author: "%{name} de l'auteur"
949 950 label_attribute_of_assigned_to: "%{name} de l'assignΓ©"
950 951 label_attribute_of_user: "%{name} de l'utilisateur"
951 952 label_attribute_of_fixed_version: "%{name} de la version cible"
952 953 label_cross_project_descendants: Avec les sous-projets
953 954 label_cross_project_tree: Avec tout l'arbre
954 955 label_cross_project_hierarchy: Avec toute la hiΓ©rarchie
955 956 label_cross_project_system: Avec tous les projets
956 957 label_gantt_progress_line: Ligne de progression
957 958 label_visibility_private: par moi uniquement
958 959 label_visibility_roles: par ces rΓ΄les uniquement
959 960 label_visibility_public: par tout le monde
960 961 label_link: Lien
961 962 label_only: seulement
962 963 label_drop_down_list: liste dΓ©roulante
963 964 label_checkboxes: cases Γ  cocher
964 965 label_radio_buttons: boutons radio
965 966 label_link_values_to: Lier les valeurs vers l'URL
966 967 label_custom_field_select_type: Selectionner le type d'objet auquel attacher le champ personnalisΓ©
967 968 label_check_for_updates: VΓ©rifier les mises Γ  jour
968 969 label_latest_compatible_version: Dernière version compatible
969 970 label_unknown_plugin: Plugin inconnu
970 971 label_add_projects: Ajouter des projets
971 972 label_users_visibility_all: Tous les utilisateurs actifs
972 973 label_users_visibility_members_of_visible_projects: Membres des projets visibles
973 974 label_edit_attachments: Modifier les fichiers attachΓ©s
974 975 label_link_copied_issue: Lier la demande copiΓ©e
975 976 label_ask: Demander
976 977 label_search_attachments_yes: Rechercher les noms et descriptions de fichiers
977 978 label_search_attachments_no: Ne pas rechercher les fichiers
978 979 label_search_attachments_only: Rechercher les fichiers uniquement
979 980 label_search_open_issues_only: Demandes ouvertes uniquement
980 981 label_email_address_plural: Emails
981 982 label_email_address_add: Ajouter une adresse email
982 983 label_enable_notifications: Activer les notifications
983 984 label_disable_notifications: DΓ©sactiver les notifications
984 985 label_blank_value: non renseignΓ©
985 986 label_parent_task_attributes: Attributs des tΓ’ches parentes
986 987 label_time_entries_visibility_all: Tous les temps passΓ©s
987 988 label_time_entries_visibility_own: Ses propres temps passΓ©s
988 989 label_member_management: Gestion des membres
989 990 label_member_management_all_roles: Tous les rΓ΄les
990 991 label_member_management_selected_roles_only: Ces rΓ΄les uniquement
991 992 label_import_issues: Importer des demandes
992 993 label_select_file_to_import: SΓ©lectionner le fichier Γ  importer
993 994 label_fields_separator: SΓ©parateur de champs
994 995 label_fields_wrapper: DΓ©limiteur de texte
995 996 label_encoding: Encodage
996 997 label_comma_char: Virgule
997 998 label_semi_colon_char: Point virgule
998 999 label_quote_char: Apostrophe
999 1000 label_double_quote_char: Double apostrophe
1000 1001 label_fields_mapping: Correspondance des champs
1001 1002 label_file_content_preview: AperΓ§u du contenu du fichier
1002 1003 label_create_missing_values: CrΓ©er les valeurs manquantes
1003 1004 label_api: API
1004 1005 label_field_format_enumeration: Liste clΓ©/valeur
1005 1006 label_default_values_for_new_users: Valeurs par dΓ©faut pour les nouveaux utilisateurs
1006 1007 label_relations: Relations
1007 1008 label_new_project_issue_tab_enabled: Afficher l'onglet "Nouvelle demande"
1008 1009 label_new_object_tab_enabled: Afficher le menu dΓ©roulant "+"
1009 1010
1010 1011 button_login: Connexion
1011 1012 button_submit: Soumettre
1012 1013 button_save: Sauvegarder
1013 1014 button_check_all: Tout cocher
1014 1015 button_uncheck_all: Tout dΓ©cocher
1015 1016 button_collapse_all: Plier tout
1016 1017 button_expand_all: DΓ©plier tout
1017 1018 button_delete: Supprimer
1018 1019 button_create: CrΓ©er
1019 1020 button_create_and_continue: CrΓ©er et continuer
1020 1021 button_test: Tester
1021 1022 button_edit: Modifier
1022 1023 button_edit_associated_wikipage: "Modifier la page wiki associΓ©e: %{page_title}"
1023 1024 button_add: Ajouter
1024 1025 button_change: Changer
1025 1026 button_apply: Appliquer
1026 1027 button_clear: Effacer
1027 1028 button_lock: Verrouiller
1028 1029 button_unlock: DΓ©verrouiller
1029 1030 button_download: TΓ©lΓ©charger
1030 1031 button_list: Lister
1031 1032 button_view: Voir
1032 1033 button_move: DΓ©placer
1033 1034 button_move_and_follow: DΓ©placer et suivre
1034 1035 button_back: Retour
1035 1036 button_cancel: Annuler
1036 1037 button_activate: Activer
1037 1038 button_sort: Trier
1038 1039 button_log_time: Saisir temps
1039 1040 button_rollback: Revenir Γ  cette version
1040 1041 button_watch: Surveiller
1041 1042 button_unwatch: Ne plus surveiller
1042 1043 button_reply: RΓ©pondre
1043 1044 button_archive: Archiver
1044 1045 button_unarchive: DΓ©sarchiver
1045 1046 button_reset: RΓ©initialiser
1046 1047 button_rename: Renommer
1047 1048 button_change_password: Changer de mot de passe
1048 1049 button_copy: Copier
1049 1050 button_copy_and_follow: Copier et suivre
1050 1051 button_annotate: Annoter
1051 1052 button_update: Mettre Γ  jour
1052 1053 button_configure: Configurer
1053 1054 button_quote: Citer
1054 1055 button_duplicate: Dupliquer
1055 1056 button_show: Afficher
1056 1057 button_hide: Cacher
1057 1058 button_edit_section: Modifier cette section
1058 1059 button_export: Exporter
1059 1060 button_delete_my_account: Supprimer mon compte
1060 1061 button_close: Fermer
1061 1062 button_reopen: RΓ©ouvrir
1062 1063 button_import: Importer
1063 1064 button_filter: Filtrer
1064 1065
1065 1066 status_active: actif
1066 1067 status_registered: enregistrΓ©
1067 1068 status_locked: verrouillΓ©
1068 1069
1069 1070 project_status_active: actif
1070 1071 project_status_closed: fermΓ©
1071 1072 project_status_archived: archivΓ©
1072 1073
1073 1074 version_status_open: ouvert
1074 1075 version_status_locked: verrouillΓ©
1075 1076 version_status_closed: fermΓ©
1076 1077
1077 1078 field_active: Actif
1078 1079
1079 1080 text_select_mail_notifications: Actions pour lesquelles une notification par e-mail est envoyΓ©e
1080 1081 text_regexp_info: ex. ^[A-Z0-9]+$
1081 1082 text_min_max_length_info: 0 pour aucune restriction
1082 1083 text_project_destroy_confirmation: Êtes-vous sûr de vouloir supprimer ce projet et toutes ses données ?
1083 1084 text_subprojects_destroy_warning: "Ses sous-projets : %{value} seront Γ©galement supprimΓ©s."
1084 1085 text_workflow_edit: SΓ©lectionner un tracker et un rΓ΄le pour Γ©diter le workflow
1085 1086 text_are_you_sure: Êtes-vous sûr ?
1086 1087 text_journal_changed: "%{label} changΓ© de %{old} Γ  %{new}"
1087 1088 text_journal_changed_no_detail: "%{label} mis Γ  jour"
1088 1089 text_journal_set_to: "%{label} mis Γ  %{value}"
1089 1090 text_journal_deleted: "%{label} %{old} supprimΓ©"
1090 1091 text_journal_added: "%{label} %{value} ajoutΓ©"
1091 1092 text_tip_issue_begin_day: tΓ’che commenΓ§ant ce jour
1092 1093 text_tip_issue_end_day: tΓ’che finissant ce jour
1093 1094 text_tip_issue_begin_end_day: tΓ’che commenΓ§ant et finissant ce jour
1094 1095 text_project_identifier_info: 'Seuls les lettres minuscules (a-z), chiffres, tirets et tirets bas sont autorisΓ©s, doit commencer par une minuscule.<br />Un fois sauvegardΓ©, l''identifiant ne pourra plus Γͺtre modifiΓ©.'
1095 1096 text_caracters_maximum: "%{count} caractères maximum."
1096 1097 text_caracters_minimum: "%{count} caractères minimum."
1097 1098 text_length_between: "Longueur comprise entre %{min} et %{max} caractères."
1098 1099 text_tracker_no_workflow: Aucun worflow n'est dΓ©fini pour ce tracker
1099 1100 text_unallowed_characters: Caractères non autorisés
1100 1101 text_comma_separated: Plusieurs valeurs possibles (sΓ©parΓ©es par des virgules).
1101 1102 text_line_separated: Plusieurs valeurs possibles (une valeur par ligne).
1102 1103 text_issues_ref_in_commit_messages: RΓ©fΓ©rencement et rΓ©solution des demandes dans les commentaires de commits
1103 1104 text_issue_added: "La demande %{id} a Γ©tΓ© soumise par %{author}."
1104 1105 text_issue_updated: "La demande %{id} a Γ©tΓ© mise Γ  jour par %{author}."
1105 1106 text_wiki_destroy_confirmation: Etes-vous sΓ»r de vouloir supprimer ce wiki et tout son contenu ?
1106 1107 text_issue_category_destroy_question: "%{count} demandes sont affectΓ©es Γ  cette catΓ©gorie. Que voulez-vous faire ?"
1107 1108 text_issue_category_destroy_assignments: N'affecter les demandes Γ  aucune autre catΓ©gorie
1108 1109 text_issue_category_reassign_to: RΓ©affecter les demandes Γ  cette catΓ©gorie
1109 1110 text_user_mail_option: "Pour les projets non sΓ©lectionnΓ©s, vous recevrez seulement des notifications pour ce que vous surveillez ou Γ  quoi vous participez (exemple: demandes dont vous Γͺtes l'auteur ou la personne assignΓ©e)."
1110 1111 text_no_configuration_data: "Les rΓ΄les, trackers, statuts et le workflow ne sont pas encore paramΓ©trΓ©s.\nIl est vivement recommandΓ© de charger le paramΓ©trage par defaut. Vous pourrez le modifier une fois chargΓ©."
1111 1112 text_load_default_configuration: Charger le paramΓ©trage par dΓ©faut
1112 1113 text_status_changed_by_changeset: "AppliquΓ© par commit %{value}."
1113 1114 text_time_logged_by_changeset: "AppliquΓ© par commit %{value}"
1114 1115 text_issues_destroy_confirmation: 'Êtes-vous sûr de vouloir supprimer la ou les demandes(s) selectionnée(s) ?'
1115 1116 text_issues_destroy_descendants_confirmation: "Cela entrainera Γ©galement la suppression de %{count} sous-tΓ’che(s)."
1116 1117 text_time_entries_destroy_confirmation: "Etes-vous sΓ»r de vouloir supprimer les temps passΓ©s sΓ©lectionnΓ©s ?"
1117 1118 text_select_project_modules: 'SΓ©lectionner les modules Γ  activer pour ce projet :'
1118 1119 text_default_administrator_account_changed: Compte administrateur par dΓ©faut changΓ©
1119 1120 text_file_repository_writable: RΓ©pertoire de stockage des fichiers accessible en Γ©criture
1120 1121 text_plugin_assets_writable: RΓ©pertoire public des plugins accessible en Γ©criture
1121 1122 text_rmagick_available: Bibliothèque RMagick présente (optionnelle)
1122 1123 text_convert_available: Binaire convert de ImageMagick prΓ©sent (optionel)
1123 1124 text_destroy_time_entries_question: "%{hours} heures ont Γ©tΓ© enregistrΓ©es sur les demandes Γ  supprimer. Que voulez-vous faire ?"
1124 1125 text_destroy_time_entries: Supprimer les heures
1125 1126 text_assign_time_entries_to_project: Reporter les heures sur le projet
1126 1127 text_reassign_time_entries: 'Reporter les heures sur cette demande:'
1127 1128 text_user_wrote: "%{value} a Γ©crit :"
1128 1129 text_enumeration_destroy_question: "La valeur Β« %{name} Β» est affectΓ©e Γ  %{count} objet(s)."
1129 1130 text_enumeration_category_reassign_to: 'RΓ©affecter les objets Γ  cette valeur:'
1130 1131 text_email_delivery_not_configured: "L'envoi de mail n'est pas configurΓ©, les notifications sont dΓ©sactivΓ©es.\nConfigurez votre serveur SMTP dans config/configuration.yml et redΓ©marrez l'application pour les activer."
1131 1132 text_repository_usernames_mapping: "Vous pouvez sΓ©lectionner ou modifier l'utilisateur Redmine associΓ© Γ  chaque nom d'utilisateur figurant dans l'historique du dΓ©pΓ΄t.\nLes utilisateurs avec le mΓͺme identifiant ou la mΓͺme adresse mail seront automatiquement associΓ©s."
1132 1133 text_diff_truncated: '... Ce diffΓ©rentiel a Γ©tΓ© tronquΓ© car il excΓ¨de la taille maximale pouvant Γͺtre affichΓ©e.'
1133 1134 text_custom_field_possible_values_info: 'Une ligne par valeur'
1134 1135 text_wiki_page_destroy_question: "Cette page possède %{descendants} sous-page(s) et descendante(s). Que voulez-vous faire ?"
1135 1136 text_wiki_page_nullify_children: "Conserver les sous-pages en tant que pages racines"
1136 1137 text_wiki_page_destroy_children: "Supprimer les sous-pages et toutes leurs descedantes"
1137 1138 text_wiki_page_reassign_children: "RΓ©affecter les sous-pages Γ  cette page"
1138 1139 text_own_membership_delete_confirmation: "Vous allez supprimer tout ou partie de vos permissions sur ce projet et ne serez peut-Γͺtre plus autorisΓ© Γ  modifier ce projet.\nEtes-vous sΓ»r de vouloir continuer ?"
1139 1140 text_zoom_in: Zoom avant
1140 1141 text_zoom_out: Zoom arrière
1141 1142 text_warn_on_leaving_unsaved: "Cette page contient du texte non sauvegardΓ© qui sera perdu si vous quittez la page."
1142 1143 text_scm_path_encoding_note: "DΓ©faut : UTF-8"
1143 1144 text_subversion_repository_note: "Exemples (en fonction des protocoles supportΓ©s) : file:///, http://, https://, svn://, svn+[tunnelscheme]://"
1144 1145 text_git_repository_note: "Chemin vers un dΓ©pΓ΄t vide et local (exemples : /gitrepo, c:\\gitrepo)"
1145 1146 text_mercurial_repository_note: "Chemin vers un dΓ©pΓ΄t local (exemples : /hgrepo, c:\\hgrepo)"
1146 1147 text_scm_command: Commande
1147 1148 text_scm_command_version: Version
1148 1149 text_scm_config: Vous pouvez configurer les commandes des SCM dans config/configuration.yml. Redémarrer l'application après modification.
1149 1150 text_scm_command_not_available: Ce SCM n'est pas disponible. Vérifier les paramètres dans la section administration.
1150 1151 text_issue_conflict_resolution_overwrite: "Appliquer quand mΓͺme ma mise Γ  jour (les notes prΓ©cΓ©dentes seront conservΓ©es mais des changements pourront Γͺtre Γ©crasΓ©s)"
1151 1152 text_issue_conflict_resolution_add_notes: "Ajouter mes notes et ignorer mes autres changements"
1152 1153 text_issue_conflict_resolution_cancel: "Annuler ma mise Γ  jour et rΓ©afficher %{link}"
1153 1154 text_account_destroy_confirmation: "Êtes-vous sûr de vouloir continuer ?\nVotre compte sera définitivement supprimé, sans aucune possibilité de le réactiver."
1154 1155 text_session_expiration_settings: "Attention : le changement de ces paramètres peut entrainer l'expiration des sessions utilisateurs en cours, y compris la vôtre."
1155 1156 text_project_closed: Ce projet est fermΓ© et accessible en lecture seule.
1156 1157 text_turning_multiple_off: "Si vous dΓ©sactivez les valeurs multiples, les valeurs multiples seront supprimΓ©es pour n'en conserver qu'une par objet."
1157 1158
1158 1159 default_role_manager: Manager
1159 1160 default_role_developer: DΓ©veloppeur
1160 1161 default_role_reporter: Rapporteur
1161 1162 default_tracker_bug: Anomalie
1162 1163 default_tracker_feature: Evolution
1163 1164 default_tracker_support: Assistance
1164 1165 default_issue_status_new: Nouveau
1165 1166 default_issue_status_in_progress: En cours
1166 1167 default_issue_status_resolved: RΓ©solu
1167 1168 default_issue_status_feedback: Commentaire
1168 1169 default_issue_status_closed: FermΓ©
1169 1170 default_issue_status_rejected: RejetΓ©
1170 1171 default_doc_category_user: Documentation utilisateur
1171 1172 default_doc_category_tech: Documentation technique
1172 1173 default_priority_low: Bas
1173 1174 default_priority_normal: Normal
1174 1175 default_priority_high: Haut
1175 1176 default_priority_urgent: Urgent
1176 1177 default_priority_immediate: ImmΓ©diat
1177 1178 default_activity_design: Conception
1178 1179 default_activity_development: DΓ©veloppement
1179 1180
1180 1181 enumeration_issue_priorities: PrioritΓ©s des demandes
1181 1182 enumeration_doc_categories: CatΓ©gories des documents
1182 1183 enumeration_activities: ActivitΓ©s (suivi du temps)
1183 1184 enumeration_system_activity: Activité système
1184 1185 description_filter: Filtre
1185 1186 description_search: Champ de recherche
1186 1187 description_choose_project: Projets
1187 1188 description_project_scope: Périmètre de recherche
1188 1189 description_notes: Notes
1189 1190 description_message_content: Contenu du message
1190 1191 description_query_sort_criteria_attribute: Critère de tri
1191 1192 description_query_sort_criteria_direction: Ordre de tri
1192 1193 description_user_mail_notification: Option de notification
1193 1194 description_available_columns: Colonnes disponibles
1194 1195 description_selected_columns: Colonnes sΓ©lectionnΓ©es
1195 1196 description_all_columns: Toutes les colonnes
1196 1197 description_issue_category_reassign: Choisir une catΓ©gorie
1197 1198 description_wiki_subpages_reassign: Choisir une nouvelle page parent
1198 1199 description_date_range_list: Choisir une pΓ©riode prΓ©dΓ©finie
1199 1200 description_date_range_interval: Choisir une pΓ©riode
1200 1201 description_date_from: Date de dΓ©but
1201 1202 description_date_to: Date de fin
1202 1203 text_repository_identifier_info: 'Seuls les lettres minuscules (a-z), chiffres, tirets et tirets bas sont autorisΓ©s.<br />Un fois sauvegardΓ©, l''identifiant ne pourra plus Γͺtre modifiΓ©.'
1203 1204 label_parent_task_attributes_derived: CalculΓ© Γ  partir des sous-tΓ’ches
1204 1205 label_parent_task_attributes_independent: IndΓ©pendent des sous-tΓ’ches
1205 1206 mail_subject_security_notification: Notification de sΓ©curitΓ©
1206 1207 mail_body_security_notification_change: ! '%{field} modifiΓ©(e).'
1207 1208 mail_body_security_notification_change_to: ! '%{field} changΓ©(e) en %{value}.'
1208 1209 mail_body_security_notification_add: ! '%{field} %{value} ajoutΓ©(e).'
1209 1210 mail_body_security_notification_remove: ! '%{field} %{value} supprimΓ©(e).'
1210 1211 mail_body_security_notification_notify_enabled: Les notifications ont Γ©tΓ© activΓ©es pour l'adresse %{value}
1211 1212 mail_body_security_notification_notify_disabled: Les notifications ont Γ©tΓ© dΓ©sactivΓ©es pour l'adresse %{value}
1212 1213 field_remote_ip: Adresse IP
1213 1214 label_no_preview: No preview available
@@ -1,4682 +1,4700
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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.expand_path('../../test_helper', __FILE__)
19 19
20 20 class IssuesControllerTest < Redmine::ControllerTest
21 21 fixtures :projects,
22 22 :users, :email_addresses, :user_preferences,
23 23 :roles,
24 24 :members,
25 25 :member_roles,
26 26 :issues,
27 27 :issue_statuses,
28 28 :issue_relations,
29 29 :versions,
30 30 :trackers,
31 31 :projects_trackers,
32 32 :issue_categories,
33 33 :enabled_modules,
34 34 :enumerations,
35 35 :attachments,
36 36 :workflows,
37 37 :custom_fields,
38 38 :custom_values,
39 39 :custom_fields_projects,
40 40 :custom_fields_trackers,
41 41 :time_entries,
42 42 :journals,
43 43 :journal_details,
44 44 :queries,
45 45 :repositories,
46 46 :changesets
47 47
48 48 include Redmine::I18n
49 49
50 50 def setup
51 51 User.current = nil
52 52 end
53 53
54 54 def test_index
55 55 with_settings :default_language => "en" do
56 56 get :index
57 57 assert_response :success
58 58
59 59 # links to visible issues
60 60 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
61 61 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
62 62 # private projects hidden
63 63 assert_select 'a[href="/issues/6"]', 0
64 64 assert_select 'a[href="/issues/4"]', 0
65 65 # project column
66 66 assert_select 'th', :text => /Project/
67 67 end
68 68 end
69 69
70 70 def test_index_should_not_list_issues_when_module_disabled
71 71 EnabledModule.where("name = 'issue_tracking' AND project_id = 1").delete_all
72 72 get :index
73 73 assert_response :success
74 74
75 75 assert_select 'a[href="/issues/1"]', 0
76 76 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
77 77 end
78 78
79 79 def test_index_should_list_visible_issues_only
80 80 get :index, :per_page => 100
81 81 assert_response :success
82 82
83 83 Issue.open.each do |issue|
84 84 assert_select "tr#issue-#{issue.id}", issue.visible? ? 1 : 0
85 85 end
86 86 end
87 87
88 88 def test_index_with_project
89 89 Setting.display_subprojects_issues = 0
90 90 get :index, :project_id => 1
91 91 assert_response :success
92 92
93 93 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
94 94 assert_select 'a[href="/issues/5"]', 0
95 95 end
96 96
97 97 def test_index_with_project_and_subprojects
98 98 Setting.display_subprojects_issues = 1
99 99 get :index, :project_id => 1
100 100 assert_response :success
101 101
102 102 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
103 103 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
104 104 assert_select 'a[href="/issues/6"]', 0
105 105 end
106 106
107 107 def test_index_with_project_and_subprojects_should_show_private_subprojects_with_permission
108 108 @request.session[:user_id] = 2
109 109 Setting.display_subprojects_issues = 1
110 110 get :index, :project_id => 1
111 111 assert_response :success
112 112
113 113 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
114 114 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
115 115 assert_select 'a[href="/issues/6"]', :text => /Issue of a private subproject/
116 116 end
117 117
118 118 def test_index_with_project_and_default_filter
119 119 get :index, :project_id => 1, :set_filter => 1
120 120 assert_response :success
121 121
122 122 # default filter
123 123 assert_query_filters [['status_id', 'o', '']]
124 124 end
125 125
126 126 def test_index_with_project_and_filter
127 127 get :index, :project_id => 1, :set_filter => 1,
128 128 :f => ['tracker_id'],
129 129 :op => {'tracker_id' => '='},
130 130 :v => {'tracker_id' => ['1']}
131 131 assert_response :success
132 132
133 133 assert_query_filters [['tracker_id', '=', '1']]
134 134 end
135 135
136 136 def test_index_with_short_filters
137 137 to_test = {
138 138 'status_id' => {
139 139 'o' => { :op => 'o', :values => [''] },
140 140 'c' => { :op => 'c', :values => [''] },
141 141 '7' => { :op => '=', :values => ['7'] },
142 142 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
143 143 '=7' => { :op => '=', :values => ['7'] },
144 144 '!3' => { :op => '!', :values => ['3'] },
145 145 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
146 146 'subject' => {
147 147 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
148 148 'o' => { :op => '=', :values => ['o'] },
149 149 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
150 150 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
151 151 'tracker_id' => {
152 152 '3' => { :op => '=', :values => ['3'] },
153 153 '=3' => { :op => '=', :values => ['3'] }},
154 154 'start_date' => {
155 155 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
156 156 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
157 157 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
158 158 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
159 159 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
160 160 '<t+2' => { :op => '<t+', :values => ['2'] },
161 161 '>t+2' => { :op => '>t+', :values => ['2'] },
162 162 't+2' => { :op => 't+', :values => ['2'] },
163 163 't' => { :op => 't', :values => [''] },
164 164 'w' => { :op => 'w', :values => [''] },
165 165 '>t-2' => { :op => '>t-', :values => ['2'] },
166 166 '<t-2' => { :op => '<t-', :values => ['2'] },
167 167 't-2' => { :op => 't-', :values => ['2'] }},
168 168 'created_on' => {
169 169 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
170 170 '<t-2' => { :op => '<t-', :values => ['2'] },
171 171 '>t-2' => { :op => '>t-', :values => ['2'] },
172 172 't-2' => { :op => 't-', :values => ['2'] }},
173 173 'cf_1' => {
174 174 'c' => { :op => '=', :values => ['c'] },
175 175 '!c' => { :op => '!', :values => ['c'] },
176 176 '!*' => { :op => '!*', :values => [''] },
177 177 '*' => { :op => '*', :values => [''] }},
178 178 'estimated_hours' => {
179 179 '=13.4' => { :op => '=', :values => ['13.4'] },
180 180 '>=45' => { :op => '>=', :values => ['45'] },
181 181 '<=125' => { :op => '<=', :values => ['125'] },
182 182 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
183 183 '!*' => { :op => '!*', :values => [''] },
184 184 '*' => { :op => '*', :values => [''] }}
185 185 }
186 186
187 187 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
188 188
189 189 to_test.each do |field, expression_and_expected|
190 190 expression_and_expected.each do |filter_expression, expected|
191 191
192 192 get :index, :set_filter => 1, field => filter_expression
193 193 assert_response :success
194 194
195 195 expected_with_default = default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}})
196 196 assert_query_filters expected_with_default.map {|f, v| [f, v[:operator], v[:values]]}
197 197 end
198 198 end
199 199 end
200 200
201 201 def test_index_with_project_and_empty_filters
202 202 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
203 203 assert_response :success
204 204
205 205 # no filter
206 206 assert_query_filters []
207 207 end
208 208
209 209 def test_index_with_project_custom_field_filter
210 210 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
211 211 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
212 212 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
213 213 filter_name = "project.cf_#{field.id}"
214 214 @request.session[:user_id] = 1
215 215
216 216 get :index, :set_filter => 1,
217 217 :f => [filter_name],
218 218 :op => {filter_name => '='},
219 219 :v => {filter_name => ['Foo']},
220 220 :c => ['project']
221 221 assert_response :success
222 222
223 223 assert_equal [3, 5], issues_in_list.map(&:project_id).uniq.sort
224 224 end
225 225
226 226 def test_index_with_query
227 227 get :index, :project_id => 1, :query_id => 5
228 228 assert_response :success
229 229 end
230 230
231 231 def test_index_with_query_grouped_by_tracker
232 232 get :index, :project_id => 1, :query_id => 6
233 233 assert_response :success
234 234 assert_select 'tr.group span.count'
235 235 end
236 236
237 237 def test_index_with_query_grouped_and_sorted_by_category
238 238 get :index, :project_id => 1, :set_filter => 1, :group_by => "category", :sort => "category"
239 239 assert_response :success
240 240 assert_select 'tr.group span.count'
241 241 end
242 242
243 243 def test_index_with_query_grouped_and_sorted_by_fixed_version
244 244 get :index, :project_id => 1, :set_filter => 1, :group_by => "fixed_version", :sort => "fixed_version"
245 245 assert_response :success
246 246 assert_select 'tr.group span.count'
247 247 end
248 248
249 249 def test_index_with_query_grouped_and_sorted_by_fixed_version_in_reverse_order
250 250 get :index, :project_id => 1, :set_filter => 1, :group_by => "fixed_version", :sort => "fixed_version:desc"
251 251 assert_response :success
252 252 assert_select 'tr.group span.count'
253 253 end
254 254
255 255 def test_index_with_query_grouped_by_list_custom_field
256 256 get :index, :project_id => 1, :query_id => 9
257 257 assert_response :success
258 258 assert_select 'tr.group span.count'
259 259 end
260 260
261 261 def test_index_with_query_grouped_by_key_value_custom_field
262 262 cf = IssueCustomField.create!(:name => 'Key', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'enumeration')
263 263 cf.enumerations << valueb = CustomFieldEnumeration.new(:name => 'Value B', :position => 1)
264 264 cf.enumerations << valuea = CustomFieldEnumeration.new(:name => 'Value A', :position => 2)
265 265 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => valueb.id)
266 266 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => valueb.id)
267 267 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => valuea.id)
268 268 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
269 269
270 270 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
271 271 assert_response :success
272 272
273 273 assert_select 'tr.group', 3
274 274 assert_select 'tr.group' do
275 275 assert_select 'span.name', :text => 'Value B'
276 276 assert_select 'span.count', :text => '2'
277 277 end
278 278 assert_select 'tr.group' do
279 279 assert_select 'span.name', :text => 'Value A'
280 280 assert_select 'span.count', :text => '1'
281 281 end
282 282 end
283 283
284 284 def test_index_with_query_grouped_by_user_custom_field
285 285 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
286 286 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
287 287 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
288 288 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
289 289 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
290 290
291 291 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
292 292 assert_response :success
293 293
294 294 assert_select 'tr.group', 3
295 295 assert_select 'tr.group' do
296 296 assert_select 'a', :text => 'John Smith'
297 297 assert_select 'span.count', :text => '1'
298 298 end
299 299 assert_select 'tr.group' do
300 300 assert_select 'a', :text => 'Dave Lopper'
301 301 assert_select 'span.count', :text => '2'
302 302 end
303 303 end
304 304
305 305 def test_index_grouped_by_boolean_custom_field_should_distinguish_blank_and_false_values
306 306 cf = IssueCustomField.create!(:name => 'Bool', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'bool')
307 307 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '1')
308 308 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
309 309 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '')
310 310
311 311 with_settings :default_language => 'en' do
312 312 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
313 313 assert_response :success
314 314 end
315 315
316 316 assert_select 'tr.group', 3
317 317 assert_select 'tr.group', :text => /Yes/
318 318 assert_select 'tr.group', :text => /No/
319 319 assert_select 'tr.group', :text => /blank/
320 320 end
321 321
322 322 def test_index_grouped_by_boolean_custom_field_with_false_group_in_first_position_should_show_the_group
323 323 cf = IssueCustomField.create!(:name => 'Bool', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'bool', :is_filter => true)
324 324 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '0')
325 325 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
326 326
327 327 with_settings :default_language => 'en' do
328 328 get :index, :project_id => 1, :set_filter => 1, "cf_#{cf.id}" => "*", :group_by => "cf_#{cf.id}"
329 329 assert_response :success
330 330 end
331 331
332 332 assert_equal [1, 2], issues_in_list.map(&:id).sort
333 333 assert_select 'tr.group', 1
334 334 assert_select 'tr.group', :text => /No/
335 335 end
336 336
337 337 def test_index_with_query_grouped_by_tracker_in_normal_order
338 338 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
339 339
340 340 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc'
341 341 assert_response :success
342 342
343 343 assert_equal ["Bug", "Feature request", "Support request"],
344 344 css_select("tr.issue td.tracker").map(&:text).uniq
345 345 end
346 346
347 347 def test_index_with_query_grouped_by_tracker_in_reverse_order
348 348 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
349 349
350 350 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc,tracker:desc'
351 351 assert_response :success
352 352
353 353 assert_equal ["Bug", "Feature request", "Support request"].reverse,
354 354 css_select("tr.issue td.tracker").map(&:text).uniq
355 355 end
356 356
357 357 def test_index_with_query_id_and_project_id_should_set_session_query
358 358 get :index, :project_id => 1, :query_id => 4
359 359 assert_response :success
360 360 assert_kind_of Hash, session[:issue_query]
361 361 assert_equal 4, session[:issue_query][:id]
362 362 assert_equal 1, session[:issue_query][:project_id]
363 363 end
364 364
365 365 def test_index_with_invalid_query_id_should_respond_404
366 366 get :index, :project_id => 1, :query_id => 999
367 367 assert_response 404
368 368 end
369 369
370 370 def test_index_with_cross_project_query_in_session_should_show_project_issues
371 371 q = IssueQuery.create!(:name => "cross_project_query", :user_id => 2, :project => nil, :column_names => ['project'])
372 372 @request.session[:issue_query] = {:id => q.id, :project_id => 1}
373 373
374 374 with_settings :display_subprojects_issues => '0' do
375 375 get :index, :project_id => 1
376 376 end
377 377 assert_response :success
378 378
379 379 assert_select 'h2', :text => q.name
380 380 assert_equal ["eCookbook"], css_select("tr.issue td.project").map(&:text).uniq
381 381 end
382 382
383 383 def test_private_query_should_not_be_available_to_other_users
384 384 q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
385 385 @request.session[:user_id] = 3
386 386
387 387 get :index, :query_id => q.id
388 388 assert_response 403
389 389 end
390 390
391 391 def test_private_query_should_be_available_to_its_user
392 392 q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
393 393 @request.session[:user_id] = 2
394 394
395 395 get :index, :query_id => q.id
396 396 assert_response :success
397 397 end
398 398
399 399 def test_public_query_should_be_available_to_other_users
400 400 q = IssueQuery.create!(:name => "public", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PUBLIC, :project => nil)
401 401 @request.session[:user_id] = 3
402 402
403 403 get :index, :query_id => q.id
404 404 assert_response :success
405 405 end
406 406
407 407 def test_index_should_omit_page_param_in_export_links
408 408 get :index, :page => 2
409 409 assert_response :success
410 410 assert_select 'a.atom[href="/issues.atom"]'
411 411 assert_select 'a.csv[href="/issues.csv"]'
412 412 assert_select 'a.pdf[href="/issues.pdf"]'
413 413 assert_select 'form#csv-export-form[action="/issues.csv"]'
414 414 end
415 415
416 416 def test_index_should_not_warn_when_not_exceeding_export_limit
417 417 with_settings :issues_export_limit => 200 do
418 418 get :index
419 419 assert_select '#csv-export-options p.icon-warning', 0
420 420 end
421 421 end
422 422
423 423 def test_index_should_warn_when_exceeding_export_limit
424 424 with_settings :issues_export_limit => 2 do
425 425 get :index
426 426 assert_select '#csv-export-options p.icon-warning', :text => %r{limit: 2}
427 427 end
428 428 end
429 429
430 430 def test_index_should_include_query_params_as_hidden_fields_in_csv_export_form
431 431 get :index, :project_id => 1, :set_filter => "1", :tracker_id => "2", :sort => 'status', :c => ["status", "priority"]
432 432
433 433 assert_select '#csv-export-form[action=?]', '/projects/ecookbook/issues.csv'
434 434 assert_select '#csv-export-form[method=?]', 'get'
435 435
436 436 assert_select '#csv-export-form' do
437 437 assert_select 'input[name=?][value=?]', 'set_filter', '1'
438 438
439 439 assert_select 'input[name=?][value=?]', 'f[]', 'tracker_id'
440 440 assert_select 'input[name=?][value=?]', 'op[tracker_id]', '='
441 441 assert_select 'input[name=?][value=?]', 'v[tracker_id][]', '2'
442 442
443 443 assert_select 'input[name=?][value=?]', 'c[]', 'status'
444 444 assert_select 'input[name=?][value=?]', 'c[]', 'priority'
445 445
446 446 assert_select 'input[name=?][value=?]', 'sort', 'status'
447 447 end
448 448
449 449 get :index, :project_id => 1, :set_filter => "1", :f => []
450 450 assert_select '#csv-export-form input[name=?][value=?]', 'f[]', ''
451 451 end
452 452
453 453 def test_index_csv
454 454 get :index, :format => 'csv'
455 455 assert_response :success
456 456
457 457 assert_equal 'text/csv; header=present', @response.content_type
458 458 assert response.body.starts_with?("#,")
459 459 lines = response.body.chomp.split("\n")
460 460 # default columns + id and project
461 461 assert_equal Setting.issue_list_default_columns.size + 2, lines[0].split(',').size
462 462 end
463 463
464 464 def test_index_csv_with_project
465 465 get :index, :project_id => 1, :format => 'csv'
466 466 assert_response :success
467 467
468 468 assert_equal 'text/csv; header=present', @response.content_type
469 469 end
470 470
471 471 def test_index_csv_without_any_filters
472 472 @request.session[:user_id] = 1
473 473 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5, :subject => 'Closed issue', :author_id => 1)
474 474 get :index, :set_filter => 1, :f => [], :format => 'csv'
475 475 assert_response :success
476 476 # -1 for headers
477 477 assert_equal Issue.count, response.body.chomp.split("\n").size - 1
478 478 end
479 479
480 480 def test_index_csv_with_description
481 481 Issue.generate!(:description => 'test_index_csv_with_description')
482 482
483 483 with_settings :default_language => 'en' do
484 484 get :index, :format => 'csv', :csv => {:description => '1'}
485 485 assert_response :success
486 486 end
487 487
488 488 assert_equal 'text/csv; header=present', response.content_type
489 489 headers = response.body.chomp.split("\n").first.split(',')
490 490 assert_include 'Description', headers
491 491 assert_include 'test_index_csv_with_description', response.body
492 492 end
493 493
494 494 def test_index_csv_with_spent_time_column
495 495 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :subject => 'test_index_csv_with_spent_time_column', :author_id => 2)
496 496 TimeEntry.create!(:project => issue.project, :issue => issue, :hours => 7.33, :user => User.find(2), :spent_on => Date.today)
497 497
498 498 get :index, :format => 'csv', :set_filter => '1', :c => %w(subject spent_hours)
499 499 assert_response :success
500 500 assert_equal 'text/csv; header=present', @response.content_type
501 501 lines = @response.body.chomp.split("\n")
502 502 assert_include "#{issue.id},#{issue.subject},7.33", lines
503 503 end
504 504
505 505 def test_index_csv_with_all_columns
506 506 get :index, :format => 'csv', :csv => {:columns => 'all'}
507 507 assert_response :success
508 508
509 509 assert_equal 'text/csv; header=present', @response.content_type
510 510 assert_match /\A#,/, response.body
511 511 lines = response.body.chomp.split("\n")
512 512 assert_equal IssueQuery.new.available_inline_columns.size, lines[0].split(',').size
513 513 end
514 514
515 515 def test_index_csv_with_multi_column_field
516 516 CustomField.find(1).update_attribute :multiple, true
517 517 issue = Issue.find(1)
518 518 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
519 519 issue.save!
520 520
521 521 get :index, :format => 'csv', :csv => {:columns => 'all'}
522 522 assert_response :success
523 523 lines = @response.body.chomp.split("\n")
524 524 assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
525 525 end
526 526
527 527 def test_index_csv_should_format_float_custom_fields_with_csv_decimal_separator
528 528 field = IssueCustomField.create!(:name => 'Float', :is_for_all => true, :tracker_ids => [1], :field_format => 'float')
529 529 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id => '185.6'})
530 530
531 531 with_settings :default_language => 'fr' do
532 532 get :index, :format => 'csv', :csv => {:columns => 'all'}
533 533 assert_response :success
534 534 issue_line = response.body.chomp.split("\n").map {|line| line.split(';')}.detect {|line| line[0]==issue.id.to_s}
535 535 assert_include '185,60', issue_line
536 536 end
537 537
538 538 with_settings :default_language => 'en' do
539 539 get :index, :format => 'csv', :csv => {:columns => 'all'}
540 540 assert_response :success
541 541 issue_line = response.body.chomp.split("\n").map {|line| line.split(',')}.detect {|line| line[0]==issue.id.to_s}
542 542 assert_include '185.60', issue_line
543 543 end
544 544 end
545 545
546 546 def test_index_csv_should_fill_parent_column_with_parent_id
547 547 Issue.delete_all
548 548 parent = Issue.generate!
549 549 child = Issue.generate!(:parent_issue_id => parent.id)
550 550
551 551 with_settings :default_language => 'en' do
552 552 get :index, :format => 'csv', :c => %w(parent)
553 553 end
554 554 lines = response.body.split("\n")
555 555 assert_include "#{child.id},#{parent.id}", lines
556 556 end
557 557
558 558 def test_index_csv_big_5
559 559 with_settings :default_language => "zh-TW" do
560 560 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88".force_encoding('UTF-8')
561 561 str_big5 = "\xa4@\xa4\xeb".force_encoding('Big5')
562 562 issue = Issue.generate!(:subject => str_utf8)
563 563
564 564 get :index, :project_id => 1,
565 565 :f => ['subject'],
566 566 :op => '=', :values => [str_utf8],
567 567 :format => 'csv'
568 568 assert_equal 'text/csv; header=present', @response.content_type
569 569 lines = @response.body.chomp.split("\n")
570 570 header = lines[0]
571 571 status = "\xaa\xac\xbaA".force_encoding('Big5')
572 572 assert_include status, header
573 573 issue_line = lines.find {|l| l =~ /^#{issue.id},/}
574 574 assert_include str_big5, issue_line
575 575 end
576 576 end
577 577
578 578 def test_index_csv_cannot_convert_should_be_replaced_big_5
579 579 with_settings :default_language => "zh-TW" do
580 580 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
581 581 issue = Issue.generate!(:subject => str_utf8)
582 582
583 583 get :index, :project_id => 1,
584 584 :f => ['subject'],
585 585 :op => '=', :values => [str_utf8],
586 586 :c => ['status', 'subject'],
587 587 :format => 'csv',
588 588 :set_filter => 1
589 589 assert_equal 'text/csv; header=present', @response.content_type
590 590 lines = @response.body.chomp.split("\n")
591 591 header = lines[0]
592 592 issue_line = lines.find {|l| l =~ /^#{issue.id},/}
593 593 s1 = "\xaa\xac\xbaA".force_encoding('Big5') # status
594 594 assert header.include?(s1)
595 595 s2 = issue_line.split(",")[2]
596 596 s3 = "\xa5H?".force_encoding('Big5') # subject
597 597 assert_equal s3, s2
598 598 end
599 599 end
600 600
601 601 def test_index_csv_tw
602 602 with_settings :default_language => "zh-TW" do
603 603 str1 = "test_index_csv_tw"
604 604 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
605 605
606 606 get :index, :project_id => 1,
607 607 :f => ['subject'],
608 608 :op => '=', :values => [str1],
609 609 :c => ['estimated_hours', 'subject'],
610 610 :format => 'csv',
611 611 :set_filter => 1
612 612 assert_equal 'text/csv; header=present', @response.content_type
613 613 lines = @response.body.chomp.split("\n")
614 614 assert_include "#{issue.id},1234.50,#{str1}", lines
615 615 end
616 616 end
617 617
618 618 def test_index_csv_fr
619 619 with_settings :default_language => "fr" do
620 620 str1 = "test_index_csv_fr"
621 621 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
622 622
623 623 get :index, :project_id => 1,
624 624 :f => ['subject'],
625 625 :op => '=', :values => [str1],
626 626 :c => ['estimated_hours', 'subject'],
627 627 :format => 'csv',
628 628 :set_filter => 1
629 629 assert_equal 'text/csv; header=present', @response.content_type
630 630 lines = @response.body.chomp.split("\n")
631 631 assert_include "#{issue.id};1234,50;#{str1}", lines
632 632 end
633 633 end
634 634
635 635 def test_index_pdf
636 636 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
637 637 with_settings :default_language => lang do
638 638
639 639 get :index
640 640 assert_response :success
641 641
642 642 get :index, :format => 'pdf'
643 643 assert_response :success
644 644 assert_equal 'application/pdf', @response.content_type
645 645
646 646 get :index, :project_id => 1, :format => 'pdf'
647 647 assert_response :success
648 648 assert_equal 'application/pdf', @response.content_type
649 649
650 650 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
651 651 assert_response :success
652 652 assert_equal 'application/pdf', @response.content_type
653 653 end
654 654 end
655 655 end
656 656
657 657 def test_index_pdf_with_query_grouped_by_list_custom_field
658 658 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
659 659 assert_response :success
660 660 assert_equal 'application/pdf', @response.content_type
661 661 end
662 662
663 663 def test_index_atom
664 664 get :index, :project_id => 'ecookbook', :format => 'atom'
665 665 assert_response :success
666 666 assert_equal 'application/atom+xml', response.content_type
667 667
668 668 assert_select 'feed' do
669 669 assert_select 'link[rel=self][href=?]', 'http://test.host/projects/ecookbook/issues.atom'
670 670 assert_select 'link[rel=alternate][href=?]', 'http://test.host/projects/ecookbook/issues'
671 671 assert_select 'entry link[href=?]', 'http://test.host/issues/1'
672 672 end
673 673 end
674 674
675 675 def test_index_should_include_back_url_input
676 676 get :index, :project_id => 'ecookbook', :foo => 'bar'
677 677 assert_response :success
678 678 assert_select 'input[name=back_url][value=?]', '/projects/ecookbook/issues?foo=bar'
679 679 end
680 680
681 681 def test_index_sort
682 682 get :index, :sort => 'tracker,id:desc'
683 683 assert_response :success
684 684
685 685 sort_params = @request.session['issues_index_sort']
686 686 assert sort_params.is_a?(String)
687 687 assert_equal 'tracker,id:desc', sort_params
688 688
689 689 assert_equal issues_in_list.sort_by {|issue| [issue.tracker.position, -issue.id]}, issues_in_list
690 690 assert_select 'table.issues.sort-by-tracker.sort-asc'
691 691 end
692 692
693 693 def test_index_sort_by_field_not_included_in_columns
694 694 Setting.issue_list_default_columns = %w(subject author)
695 695 get :index, :sort => 'tracker'
696 696 end
697 697
698 698 def test_index_sort_by_assigned_to
699 699 get :index, :sort => 'assigned_to'
700 700 assert_response :success
701 701
702 702 assignees = issues_in_list.map(&:assigned_to).compact
703 703 assert_equal assignees.sort, assignees
704 704 assert_select 'table.issues.sort-by-assigned-to.sort-asc'
705 705 end
706 706
707 707 def test_index_sort_by_assigned_to_desc
708 708 get :index, :sort => 'assigned_to:desc'
709 709 assert_response :success
710 710
711 711 assignees = issues_in_list.map(&:assigned_to).compact
712 712 assert_equal assignees.sort.reverse, assignees
713 713 assert_select 'table.issues.sort-by-assigned-to.sort-desc'
714 714 end
715 715
716 716 def test_index_group_by_assigned_to
717 717 get :index, :group_by => 'assigned_to', :sort => 'priority'
718 718 assert_response :success
719 719 end
720 720
721 721 def test_index_sort_by_author
722 722 get :index, :sort => 'author', :c => ['author']
723 723 assert_response :success
724 724
725 725 authors = issues_in_list.map(&:author)
726 726 assert_equal authors.sort, authors
727 727 end
728 728
729 729 def test_index_sort_by_author_desc
730 730 get :index, :sort => 'author:desc'
731 731 assert_response :success
732 732
733 733 authors = issues_in_list.map(&:author)
734 734 assert_equal authors.sort.reverse, authors
735 735 end
736 736
737 737 def test_index_group_by_author
738 738 get :index, :group_by => 'author', :sort => 'priority'
739 739 assert_response :success
740 740 end
741 741
742 742 def test_index_sort_by_spent_hours
743 743 get :index, :sort => 'spent_hours:desc'
744 744 assert_response :success
745 745 hours = issues_in_list.map(&:spent_hours)
746 746 assert_equal hours.sort.reverse, hours
747 747 end
748 748
749 749 def test_index_sort_by_total_spent_hours
750 750 get :index, :sort => 'total_spent_hours:desc'
751 751 assert_response :success
752 752 hours = issues_in_list.map(&:total_spent_hours)
753 753 assert_equal hours.sort.reverse, hours
754 754 end
755 755
756 756 def test_index_sort_by_total_estimated_hours
757 757 get :index, :sort => 'total_estimated_hours:desc'
758 758 assert_response :success
759 759 hours = issues_in_list.map(&:total_estimated_hours)
760 760 assert_equal hours.sort.reverse, hours
761 761 end
762 762
763 763 def test_index_sort_by_user_custom_field
764 764 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
765 765 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
766 766 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
767 767 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
768 768 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
769 769
770 770 get :index, :project_id => 1, :set_filter => 1, :sort => "cf_#{cf.id},id"
771 771 assert_response :success
772 772
773 773 assert_equal [2, 3, 1], issues_in_list.select {|issue| issue.custom_field_value(cf).present?}.map(&:id)
774 774 end
775 775
776 776 def test_index_with_columns
777 777 columns = ['tracker', 'subject', 'assigned_to']
778 778 get :index, :set_filter => 1, :c => columns
779 779 assert_response :success
780 780
781 781 # query should use specified columns + id and checkbox
782 782 assert_select 'table.issues thead th', columns.size + 2
783 783
784 784 # columns should be stored in session
785 785 assert_kind_of Hash, session[:issue_query]
786 786 assert_kind_of Array, session[:issue_query][:column_names]
787 787 assert_equal columns, session[:issue_query][:column_names].map(&:to_s)
788 788
789 789 # ensure only these columns are kept in the selected columns list
790 790 assert_select 'select#selected_columns option' do
791 791 assert_select 'option', 3
792 792 assert_select 'option[value=tracker]'
793 793 assert_select 'option[value=project]', 0
794 794 end
795 795 end
796 796
797 797 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
798 798 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
799 799 get :index, :set_filter => 1
800 800
801 801 # query should use specified columns
802 802 assert_equal ["#", "Project", "Tracker", "Subject", "Assignee"], columns_in_issues_list
803 803 end
804 804
805 805 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
806 806 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
807 807 columns = ['id', 'tracker', 'subject', 'assigned_to']
808 808 get :index, :set_filter => 1, :c => columns
809 809
810 810 # query should use specified columns
811 811 assert_equal ["#", "Tracker", "Subject", "Assignee"], columns_in_issues_list
812 812 end
813 813
814 814 def test_index_with_default_columns_should_respect_default_columns_order
815 815 columns = ['assigned_to', 'subject', 'status', 'tracker']
816 816 with_settings :issue_list_default_columns => columns do
817 817 get :index, :project_id => 1, :set_filter => 1
818 818
819 819 assert_equal ["#", "Assignee", "Subject", "Status", "Tracker"], columns_in_issues_list
820 820 end
821 821 end
822 822
823 823 def test_index_with_custom_field_column
824 824 columns = %w(tracker subject cf_2)
825 825 get :index, :set_filter => 1, :c => columns
826 826 assert_response :success
827 827
828 828 # query should use specified columns
829 829 assert_equal ["#", "Tracker", "Subject", "Searchable field"], columns_in_issues_list
830 830 assert_select 'table.issues td.cf_2.string'
831 831 end
832 832
833 833 def test_index_with_multi_custom_field_column
834 834 field = CustomField.find(1)
835 835 field.update_attribute :multiple, true
836 836 issue = Issue.find(1)
837 837 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
838 838 issue.save!
839 839
840 840 get :index, :set_filter => 1, :c => %w(tracker subject cf_1)
841 841 assert_response :success
842 842
843 843 assert_select 'table.issues td.cf_1', :text => 'MySQL, Oracle'
844 844 end
845 845
846 846 def test_index_with_multi_user_custom_field_column
847 847 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
848 848 :tracker_ids => [1], :is_for_all => true)
849 849 issue = Issue.find(1)
850 850 issue.custom_field_values = {field.id => ['2', '3']}
851 851 issue.save!
852 852
853 853 get :index, :set_filter => 1, :c => ['tracker', 'subject', "cf_#{field.id}"]
854 854 assert_response :success
855 855
856 856 assert_select "table.issues td.cf_#{field.id}" do
857 857 assert_select 'a', 2
858 858 assert_select 'a[href=?]', '/users/2', :text => 'John Smith'
859 859 assert_select 'a[href=?]', '/users/3', :text => 'Dave Lopper'
860 860 end
861 861 end
862 862
863 863 def test_index_with_date_column
864 864 with_settings :date_format => '%d/%m/%Y' do
865 865 Issue.find(1).update_attribute :start_date, '1987-08-24'
866 866 get :index, :set_filter => 1, :c => %w(start_date)
867 867 assert_select "table.issues td.start_date", :text => '24/08/1987'
868 868 end
869 869 end
870 870
871 871 def test_index_with_done_ratio_column
872 872 Issue.find(1).update_attribute :done_ratio, 40
873 873 get :index, :set_filter => 1, :c => %w(done_ratio)
874 874 assert_select 'table.issues td.done_ratio' do
875 875 assert_select 'table.progress' do
876 876 assert_select 'td.closed[style=?]', 'width: 40%;'
877 877 end
878 878 end
879 879 end
880 880
881 881 def test_index_with_spent_hours_column
882 882 Issue.expects(:load_visible_spent_hours).once
883 883 get :index, :set_filter => 1, :c => %w(subject spent_hours)
884 884 assert_select 'table.issues tr#issue-3 td.spent_hours', :text => '1.00'
885 885 end
886 886
887 887 def test_index_with_total_spent_hours_column
888 888 Issue.expects(:load_visible_total_spent_hours).once
889 889 get :index, :set_filter => 1, :c => %w(subject total_spent_hours)
890 890 assert_select 'table.issues tr#issue-3 td.total_spent_hours', :text => '1.00'
891 891 end
892 892
893 893 def test_index_with_total_estimated_hours_column
894 894 get :index, :set_filter => 1, :c => %w(subject total_estimated_hours)
895 895 assert_select 'table.issues td.total_estimated_hours'
896 896 end
897 897
898 898 def test_index_should_not_show_spent_hours_column_without_permission
899 899 Role.anonymous.remove_permission! :view_time_entries
900 900 get :index, :set_filter => 1, :c => %w(subject spent_hours)
901 901 assert_select 'td.spent_hours', 0
902 902 end
903 903
904 904 def test_index_with_fixed_version_column
905 905 get :index, :set_filter => 1, :c => %w(fixed_version)
906 906 assert_select 'table.issues td.fixed_version' do
907 907 assert_select 'a[href=?]', '/versions/2', :text => 'eCookbook - 1.0'
908 908 end
909 909 end
910 910
911 911 def test_index_with_relations_column
912 912 IssueRelation.delete_all
913 913 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(7))
914 914 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(8), :issue_to => Issue.find(1))
915 915 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(1), :issue_to => Issue.find(11))
916 916 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(12), :issue_to => Issue.find(2))
917 917
918 918 get :index, :set_filter => 1, :c => %w(subject relations)
919 919 assert_response :success
920 920 assert_select "tr#issue-1 td.relations" do
921 921 assert_select "span", 3
922 922 assert_select "span", :text => "Related to #7"
923 923 assert_select "span", :text => "Related to #8"
924 924 assert_select "span", :text => "Blocks #11"
925 925 end
926 926 assert_select "tr#issue-2 td.relations" do
927 927 assert_select "span", 1
928 928 assert_select "span", :text => "Blocked by #12"
929 929 end
930 930 assert_select "tr#issue-3 td.relations" do
931 931 assert_select "span", 0
932 932 end
933 933
934 934 get :index, :set_filter => 1, :c => %w(relations), :format => 'csv'
935 935 assert_response :success
936 936 assert_equal 'text/csv; header=present', response.content_type
937 937 lines = response.body.chomp.split("\n")
938 938 assert_include '1,"Related to #7, Related to #8, Blocks #11"', lines
939 939 assert_include '2,Blocked by #12', lines
940 940 assert_include '3,""', lines
941 941
942 942 get :index, :set_filter => 1, :c => %w(subject relations), :format => 'pdf'
943 943 assert_response :success
944 944 assert_equal 'application/pdf', response.content_type
945 945 end
946 946
947 947 def test_index_with_description_column
948 948 get :index, :set_filter => 1, :c => %w(subject description)
949 949
950 950 assert_select 'table.issues thead th', 3 # columns: chekbox + id + subject
951 951 assert_select 'td.description[colspan="3"]', :text => 'Unable to print recipes'
952 952
953 953 get :index, :set_filter => 1, :c => %w(subject description), :format => 'pdf'
954 954 assert_response :success
955 955 assert_equal 'application/pdf', response.content_type
956 956 end
957 957
958 958 def test_index_with_parent_column
959 959 Issue.delete_all
960 960 parent = Issue.generate!
961 961 child = Issue.generate!(:parent_issue_id => parent.id)
962 962
963 963 get :index, :c => %w(parent)
964 964
965 965 assert_select 'td.parent', :text => "#{parent.tracker} ##{parent.id}"
966 966 assert_select 'td.parent a[title=?]', parent.subject
967 967 end
968 968
969 969 def test_index_with_estimated_hours_total
970 970 Issue.delete_all
971 971 Issue.generate!(:estimated_hours => 5.5)
972 972 Issue.generate!(:estimated_hours => 1.1)
973 973
974 974 get :index, :t => %w(estimated_hours)
975 975 assert_response :success
976 976 assert_select '.query-totals'
977 977 assert_select '.total-for-estimated-hours span.value', :text => '6.60'
978 978 assert_select 'input[type=checkbox][name=?][value=estimated_hours][checked=checked]', 't[]'
979 979 end
980 980
981 981 def test_index_with_grouped_query_and_estimated_hours_total
982 982 Issue.delete_all
983 983 Issue.generate!(:estimated_hours => 5.5, :category_id => 1)
984 984 Issue.generate!(:estimated_hours => 2.3, :category_id => 1)
985 985 Issue.generate!(:estimated_hours => 1.1, :category_id => 2)
986 986 Issue.generate!(:estimated_hours => 4.6)
987 987
988 988 get :index, :t => %w(estimated_hours), :group_by => 'category'
989 989 assert_response :success
990 990 assert_select '.query-totals'
991 991 assert_select '.query-totals .total-for-estimated-hours span.value', :text => '13.50'
992 992 assert_select 'tr.group', :text => /Printing/ do
993 993 assert_select '.total-for-estimated-hours span.value', :text => '7.80'
994 994 end
995 995 assert_select 'tr.group', :text => /Recipes/ do
996 996 assert_select '.total-for-estimated-hours span.value', :text => '1.10'
997 997 end
998 998 assert_select 'tr.group', :text => /blank/ do
999 999 assert_select '.total-for-estimated-hours span.value', :text => '4.60'
1000 1000 end
1001 1001 end
1002 1002
1003 1003 def test_index_with_int_custom_field_total
1004 1004 field = IssueCustomField.generate!(:field_format => 'int', :is_for_all => true)
1005 1005 CustomValue.create!(:customized => Issue.find(1), :custom_field => field, :value => '2')
1006 1006 CustomValue.create!(:customized => Issue.find(2), :custom_field => field, :value => '7')
1007 1007
1008 1008 get :index, :t => ["cf_#{field.id}"]
1009 1009 assert_response :success
1010 1010 assert_select '.query-totals'
1011 1011 assert_select ".total-for-cf-#{field.id} span.value", :text => '9'
1012 1012 end
1013 1013
1014 1014 def test_index_totals_should_default_to_settings
1015 1015 with_settings :issue_list_default_totals => ['estimated_hours'] do
1016 1016 get :index
1017 1017 assert_response :success
1018 1018 assert_select '.total-for-estimated-hours span.value'
1019 1019 assert_select '.query-totals>span', 1
1020 1020 end
1021 1021 end
1022 1022
1023 1023 def test_index_send_html_if_query_is_invalid
1024 1024 get :index, :f => ['start_date'], :op => {:start_date => '='}
1025 1025 assert_equal 'text/html', @response.content_type
1026 1026 assert_select_error /Start date cannot be blank/i
1027 1027 end
1028 1028
1029 1029 def test_index_send_nothing_if_query_is_invalid
1030 1030 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
1031 1031 assert_equal 'text/csv', @response.content_type
1032 1032 assert @response.body.blank?
1033 1033 end
1034 1034
1035 1035 def test_index_should_include_new_issue_link
1036 1036 @request.session[:user_id] = 2
1037 1037 get :index, :project_id => 1
1038 1038 assert_select '#content a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
1039 1039 end
1040 1040
1041 1041 def test_index_should_not_include_new_issue_link_for_project_without_trackers
1042 1042 Project.find(1).trackers.clear
1043 1043
1044 1044 @request.session[:user_id] = 2
1045 1045 get :index, :project_id => 1
1046 1046 assert_select '#content a.new-issue', 0
1047 1047 end
1048 1048
1049 1049 def test_index_should_not_include_new_issue_link_for_users_with_copy_issues_permission_only
1050 1050 role = Role.find(1)
1051 1051 role.remove_permission! :add_issues
1052 1052 role.add_permission! :copy_issues
1053 1053
1054 1054 @request.session[:user_id] = 2
1055 1055 get :index, :project_id => 1
1056 1056 assert_select '#content a.new-issue', 0
1057 1057 end
1058 1058
1059 1059 def test_index_without_project_should_include_new_issue_link
1060 1060 @request.session[:user_id] = 2
1061 1061 get :index
1062 1062 assert_select '#content a.new-issue[href="/issues/new"]', :text => 'New issue'
1063 1063 end
1064 1064
1065 1065 def test_index_should_not_include_new_issue_tab_when_disabled
1066 1066 with_settings :new_item_menu_tab => '0' do
1067 1067 @request.session[:user_id] = 2
1068 1068 get :index, :project_id => 1
1069 1069 assert_select '#main-menu a.new-issue', 0
1070 1070 end
1071 1071 end
1072 1072
1073 1073 def test_index_should_include_new_issue_tab_when_enabled
1074 1074 with_settings :new_item_menu_tab => '1' do
1075 1075 @request.session[:user_id] = 2
1076 1076 get :index, :project_id => 1
1077 1077 assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
1078 1078 end
1079 1079 end
1080 1080
1081 1081 def test_new_should_have_new_issue_tab_as_current_menu_item
1082 1082 with_settings :new_item_menu_tab => '1' do
1083 1083 @request.session[:user_id] = 2
1084 1084 get :new, :project_id => 1
1085 1085 assert_select '#main-menu a.new-issue.selected'
1086 1086 end
1087 1087 end
1088 1088
1089 1089 def test_index_should_not_include_new_issue_tab_for_project_without_trackers
1090 1090 with_settings :new_item_menu_tab => '1' do
1091 1091 Project.find(1).trackers.clear
1092 1092
1093 1093 @request.session[:user_id] = 2
1094 1094 get :index, :project_id => 1
1095 1095 assert_select '#main-menu a.new-issue', 0
1096 1096 end
1097 1097 end
1098 1098
1099 1099 def test_index_should_not_include_new_issue_tab_for_users_with_copy_issues_permission_only
1100 1100 with_settings :new_item_menu_tab => '1' do
1101 1101 role = Role.find(1)
1102 1102 role.remove_permission! :add_issues
1103 1103 role.add_permission! :copy_issues
1104 1104
1105 1105 @request.session[:user_id] = 2
1106 1106 get :index, :project_id => 1
1107 1107 assert_select '#main-menu a.new-issue', 0
1108 1108 end
1109 1109 end
1110 1110
1111 1111 def test_show_by_anonymous
1112 1112 get :show, :id => 1
1113 1113 assert_response :success
1114 1114
1115 1115 assert_select 'div.issue div.description', :text => /Unable to print recipes/
1116 1116 # anonymous role is allowed to add a note
1117 1117 assert_select 'form#issue-form' do
1118 1118 assert_select 'fieldset' do
1119 1119 assert_select 'legend', :text => 'Notes'
1120 1120 assert_select 'textarea[name=?]', 'issue[notes]'
1121 1121 end
1122 1122 end
1123 1123 assert_select 'title', :text => "Bug #1: Cannot print recipes - eCookbook - Redmine"
1124 1124 end
1125 1125
1126 1126 def test_show_by_manager
1127 1127 @request.session[:user_id] = 2
1128 1128 get :show, :id => 1
1129 1129
1130 1130 assert_select 'a', :text => /Quote/
1131 1131 assert_select 'form#issue-form' do
1132 1132 assert_select 'fieldset' do
1133 1133 assert_select 'legend', :text => 'Change properties'
1134 1134 assert_select 'input[name=?]', 'issue[subject]'
1135 1135 end
1136 1136 assert_select 'fieldset' do
1137 1137 assert_select 'legend', :text => 'Log time'
1138 1138 assert_select 'input[name=?]', 'time_entry[hours]'
1139 1139 end
1140 1140 assert_select 'fieldset' do
1141 1141 assert_select 'legend', :text => 'Notes'
1142 1142 assert_select 'textarea[name=?]', 'issue[notes]'
1143 1143 end
1144 1144 end
1145 1145 end
1146 1146
1147 1147 def test_show_should_display_update_form
1148 1148 @request.session[:user_id] = 2
1149 1149 get :show, :id => 1
1150 1150 assert_response :success
1151 1151
1152 1152 assert_select 'form#issue-form' do
1153 1153 assert_select 'input[name=?]', 'issue[is_private]'
1154 1154 assert_select 'select[name=?]', 'issue[project_id]'
1155 1155 assert_select 'select[name=?]', 'issue[tracker_id]'
1156 1156 assert_select 'input[name=?]', 'issue[subject]'
1157 1157 assert_select 'textarea[name=?]', 'issue[description]'
1158 1158 assert_select 'select[name=?]', 'issue[status_id]'
1159 1159 assert_select 'select[name=?]', 'issue[priority_id]'
1160 1160 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1161 1161 assert_select 'select[name=?]', 'issue[category_id]'
1162 1162 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1163 1163 assert_select 'input[name=?]', 'issue[parent_issue_id]'
1164 1164 assert_select 'input[name=?]', 'issue[start_date]'
1165 1165 assert_select 'input[name=?]', 'issue[due_date]'
1166 1166 assert_select 'select[name=?]', 'issue[done_ratio]'
1167 1167 assert_select 'input[name=?]', 'issue[custom_field_values][2]'
1168 1168 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1169 1169 assert_select 'textarea[name=?]', 'issue[notes]'
1170 1170 end
1171 1171 end
1172 1172
1173 1173 def test_show_should_display_update_form_with_minimal_permissions
1174 1174 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
1175 1175 WorkflowTransition.where(:role_id => 1).delete_all
1176 1176
1177 1177 @request.session[:user_id] = 2
1178 1178 get :show, :id => 1
1179 1179 assert_response :success
1180 1180
1181 1181 assert_select 'form#issue-form' do
1182 1182 assert_select 'input[name=?]', 'issue[is_private]', 0
1183 1183 assert_select 'select[name=?]', 'issue[project_id]', 0
1184 1184 assert_select 'select[name=?]', 'issue[tracker_id]', 0
1185 1185 assert_select 'input[name=?]', 'issue[subject]', 0
1186 1186 assert_select 'textarea[name=?]', 'issue[description]', 0
1187 1187 assert_select 'select[name=?]', 'issue[status_id]', 0
1188 1188 assert_select 'select[name=?]', 'issue[priority_id]', 0
1189 1189 assert_select 'select[name=?]', 'issue[assigned_to_id]', 0
1190 1190 assert_select 'select[name=?]', 'issue[category_id]', 0
1191 1191 assert_select 'select[name=?]', 'issue[fixed_version_id]', 0
1192 1192 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
1193 1193 assert_select 'input[name=?]', 'issue[start_date]', 0
1194 1194 assert_select 'input[name=?]', 'issue[due_date]', 0
1195 1195 assert_select 'select[name=?]', 'issue[done_ratio]', 0
1196 1196 assert_select 'input[name=?]', 'issue[custom_field_values][2]', 0
1197 1197 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1198 1198 assert_select 'textarea[name=?]', 'issue[notes]'
1199 1199 end
1200 1200 end
1201 1201
1202 1202 def test_show_should_not_display_update_form_without_permissions
1203 1203 Role.find(1).update_attribute :permissions, [:view_issues]
1204 1204
1205 1205 @request.session[:user_id] = 2
1206 1206 get :show, :id => 1
1207 1207 assert_response :success
1208 1208
1209 1209 assert_select 'form#issue-form', 0
1210 1210 end
1211 1211
1212 1212 def test_update_form_should_not_display_inactive_enumerations
1213 1213 assert !IssuePriority.find(15).active?
1214 1214
1215 1215 @request.session[:user_id] = 2
1216 1216 get :show, :id => 1
1217 1217 assert_response :success
1218 1218
1219 1219 assert_select 'form#issue-form' do
1220 1220 assert_select 'select[name=?]', 'issue[priority_id]' do
1221 1221 assert_select 'option[value="4"]'
1222 1222 assert_select 'option[value="15"]', 0
1223 1223 end
1224 1224 end
1225 1225 end
1226 1226
1227 1227 def test_update_form_should_allow_attachment_upload
1228 1228 @request.session[:user_id] = 2
1229 1229 get :show, :id => 1
1230 1230
1231 1231 assert_select 'form#issue-form[method=post][enctype="multipart/form-data"]' do
1232 1232 assert_select 'input[type=file][name=?]', 'attachments[dummy][file]'
1233 1233 end
1234 1234 end
1235 1235
1236 1236 def test_show_should_deny_anonymous_access_without_permission
1237 1237 Role.anonymous.remove_permission!(:view_issues)
1238 1238 get :show, :id => 1
1239 1239 assert_response :redirect
1240 1240 end
1241 1241
1242 1242 def test_show_should_deny_anonymous_access_to_private_issue
1243 1243 Issue.where(:id => 1).update_all(["is_private = ?", true])
1244 1244 get :show, :id => 1
1245 1245 assert_response :redirect
1246 1246 end
1247 1247
1248 1248 def test_show_should_deny_non_member_access_without_permission
1249 1249 Role.non_member.remove_permission!(:view_issues)
1250 1250 @request.session[:user_id] = 9
1251 1251 get :show, :id => 1
1252 1252 assert_response 403
1253 1253 end
1254 1254
1255 1255 def test_show_should_deny_non_member_access_to_private_issue
1256 1256 Issue.where(:id => 1).update_all(["is_private = ?", true])
1257 1257 @request.session[:user_id] = 9
1258 1258 get :show, :id => 1
1259 1259 assert_response 403
1260 1260 end
1261 1261
1262 1262 def test_show_should_deny_member_access_without_permission
1263 1263 Role.find(1).remove_permission!(:view_issues)
1264 1264 @request.session[:user_id] = 2
1265 1265 get :show, :id => 1
1266 1266 assert_response 403
1267 1267 end
1268 1268
1269 1269 def test_show_should_deny_member_access_to_private_issue_without_permission
1270 1270 Issue.where(:id => 1).update_all(["is_private = ?", true])
1271 1271 @request.session[:user_id] = 3
1272 1272 get :show, :id => 1
1273 1273 assert_response 403
1274 1274 end
1275 1275
1276 1276 def test_show_should_allow_author_access_to_private_issue
1277 1277 Issue.where(:id => 1).update_all(["is_private = ?, author_id = 3", true])
1278 1278 @request.session[:user_id] = 3
1279 1279 get :show, :id => 1
1280 1280 assert_response :success
1281 1281 end
1282 1282
1283 1283 def test_show_should_allow_assignee_access_to_private_issue
1284 1284 Issue.where(:id => 1).update_all(["is_private = ?, assigned_to_id = 3", true])
1285 1285 @request.session[:user_id] = 3
1286 1286 get :show, :id => 1
1287 1287 assert_response :success
1288 1288 end
1289 1289
1290 1290 def test_show_should_allow_member_access_to_private_issue_with_permission
1291 1291 Issue.where(:id => 1).update_all(["is_private = ?", true])
1292 1292 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
1293 1293 @request.session[:user_id] = 3
1294 1294 get :show, :id => 1
1295 1295 assert_response :success
1296 1296 end
1297 1297
1298 1298 def test_show_should_not_disclose_relations_to_invisible_issues
1299 1299 Setting.cross_project_issue_relations = '1'
1300 1300 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
1301 1301 # Relation to a private project issue
1302 1302 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
1303 1303
1304 1304 get :show, :id => 1
1305 1305 assert_response :success
1306 1306
1307 1307 assert_select 'div#relations' do
1308 1308 assert_select 'a', :text => /#2$/
1309 1309 assert_select 'a', :text => /#4$/, :count => 0
1310 1310 end
1311 1311 end
1312 1312
1313 1313 def test_show_should_list_subtasks
1314 1314 Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1315 1315
1316 1316 get :show, :id => 1
1317 1317 assert_response :success
1318 1318
1319 1319 assert_select 'div#issue_tree' do
1320 1320 assert_select 'td.subject', :text => /Child Issue/
1321 1321 end
1322 1322 end
1323 1323
1324 1324 def test_show_should_list_parents
1325 1325 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1326 1326
1327 1327 get :show, :id => issue.id
1328 1328 assert_response :success
1329 1329
1330 1330 assert_select 'div.subject' do
1331 1331 assert_select 'h3', 'Child Issue'
1332 1332 assert_select 'a[href="/issues/1"]'
1333 1333 end
1334 1334 end
1335 1335
1336 1336 def test_show_should_not_display_prev_next_links_without_query_in_session
1337 1337 get :show, :id => 1
1338 1338 assert_response :success
1339 1339
1340 1340 assert_select 'div.next-prev-links', 0
1341 1341 end
1342 1342
1343 1343 def test_show_should_display_prev_next_links_with_query_in_session
1344 1344 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1345 1345 @request.session['issues_index_sort'] = 'id'
1346 1346
1347 1347 with_settings :display_subprojects_issues => '0' do
1348 1348 get :show, :id => 3
1349 1349 end
1350 1350 assert_response :success
1351 1351
1352 1352 count = Issue.open.visible.count
1353 1353
1354 1354 # Previous and next issues for all projects
1355 1355 assert_select 'div.next-prev-links' do
1356 1356 assert_select 'a[href="/issues/2"]', :text => /Previous/
1357 1357 assert_select 'a[href="/issues/5"]', :text => /Next/
1358 1358 assert_select 'span.position', :text => "3 of #{count}"
1359 1359 end
1360 1360 end
1361 1361
1362 1362 def test_show_should_display_prev_next_links_with_saved_query_in_session
1363 1363 query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC, :user_id => 1,
1364 1364 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
1365 1365 :sort_criteria => [['id', 'asc']])
1366 1366 @request.session[:query] = {:id => query.id, :project_id => nil}
1367 1367
1368 1368 get :show, :id => 11
1369 1369 assert_response :success
1370 1370
1371 1371 # Previous and next issues for all projects
1372 1372 assert_select 'div.next-prev-links' do
1373 1373 assert_select 'a[href="/issues/8"]', :text => /Previous/
1374 1374 assert_select 'a[href="/issues/12"]', :text => /Next/
1375 1375 end
1376 1376 end
1377 1377
1378 1378 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
1379 1379 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1380 1380
1381 1381 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
1382 1382 @request.session['issues_index_sort'] = assoc_sort
1383 1383
1384 1384 get :show, :id => 3
1385 1385 assert_response :success, "Wrong response status for #{assoc_sort} sort"
1386 1386
1387 1387 assert_select 'div.next-prev-links' do
1388 1388 assert_select 'a', :text => /(Previous|Next)/
1389 1389 end
1390 1390 end
1391 1391 end
1392 1392
1393 1393 def test_show_should_display_prev_next_links_with_project_query_in_session
1394 1394 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1395 1395 @request.session['issues_index_sort'] = 'id'
1396 1396
1397 1397 with_settings :display_subprojects_issues => '0' do
1398 1398 get :show, :id => 3
1399 1399 end
1400 1400 assert_response :success
1401 1401
1402 1402 # Previous and next issues inside project
1403 1403 assert_select 'div.next-prev-links' do
1404 1404 assert_select 'a[href="/issues/2"]', :text => /Previous/
1405 1405 assert_select 'a[href="/issues/7"]', :text => /Next/
1406 1406 end
1407 1407 end
1408 1408
1409 1409 def test_show_should_not_display_prev_link_for_first_issue
1410 1410 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1411 1411 @request.session['issues_index_sort'] = 'id'
1412 1412
1413 1413 with_settings :display_subprojects_issues => '0' do
1414 1414 get :show, :id => 1
1415 1415 end
1416 1416 assert_response :success
1417 1417
1418 1418 assert_select 'div.next-prev-links' do
1419 1419 assert_select 'a', :text => /Previous/, :count => 0
1420 1420 assert_select 'a[href="/issues/2"]', :text => /Next/
1421 1421 end
1422 1422 end
1423 1423
1424 1424 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1425 1425 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1426 1426 @request.session['issues_index_sort'] = 'id'
1427 1427
1428 1428 get :show, :id => 1
1429 1429 assert_response :success
1430 1430
1431 1431 assert_select 'a', :text => /Previous/, :count => 0
1432 1432 assert_select 'a', :text => /Next/, :count => 0
1433 1433 end
1434 1434
1435 1435 def test_show_show_should_display_prev_next_links_with_query_sort_by_user_custom_field
1436 1436 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
1437 1437 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
1438 1438 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
1439 1439 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
1440 1440 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
1441 1441
1442 1442 query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC, :user_id => 1, :filters => {},
1443 1443 :sort_criteria => [["cf_#{cf.id}", 'asc'], ['id', 'asc']])
1444 1444 @request.session[:query] = {:id => query.id, :project_id => nil}
1445 1445
1446 1446 get :show, :id => 3
1447 1447 assert_response :success
1448 1448
1449 1449 assert_select 'div.next-prev-links' do
1450 1450 assert_select 'a[href="/issues/2"]', :text => /Previous/
1451 1451 assert_select 'a[href="/issues/1"]', :text => /Next/
1452 1452 end
1453 1453 end
1454 1454
1455 1455 def test_show_should_display_prev_next_links_when_request_has_previous_and_next_issue_ids_params
1456 1456 get :show, :id => 1, :prev_issue_id => 1, :next_issue_id => 3, :issue_position => 2, :issue_count => 4
1457 1457 assert_response :success
1458 1458
1459 1459 assert_select 'div.next-prev-links' do
1460 1460 assert_select 'a[href="/issues/1"]', :text => /Previous/
1461 1461 assert_select 'a[href="/issues/3"]', :text => /Next/
1462 1462 assert_select 'span.position', :text => "2 of 4"
1463 1463 end
1464 1464 end
1465 1465
1466 1466 def test_show_should_display_category_field_if_categories_are_defined
1467 1467 Issue.update_all :category_id => nil
1468 1468
1469 1469 get :show, :id => 1
1470 1470 assert_response :success
1471 1471 assert_select '.attributes .category'
1472 1472 end
1473 1473
1474 1474 def test_show_should_not_display_category_field_if_no_categories_are_defined
1475 1475 Project.find(1).issue_categories.delete_all
1476 1476
1477 1477 get :show, :id => 1
1478 1478 assert_response :success
1479 1479 assert_select 'table.attributes .category', 0
1480 1480 end
1481 1481
1482 1482 def test_show_should_display_link_to_the_assignee
1483 1483 get :show, :id => 2
1484 1484 assert_response :success
1485 1485 assert_select '.assigned-to' do
1486 1486 assert_select 'a[href="/users/3"]'
1487 1487 end
1488 1488 end
1489 1489
1490 1490 def test_show_should_display_visible_changesets_from_other_projects
1491 1491 project = Project.find(2)
1492 1492 issue = project.issues.first
1493 1493 issue.changeset_ids = [102]
1494 1494 issue.save!
1495 1495 # changesets from other projects should be displayed even if repository
1496 1496 # is disabled on issue's project
1497 1497 project.disable_module! :repository
1498 1498
1499 1499 @request.session[:user_id] = 2
1500 1500 get :show, :id => issue.id
1501 1501
1502 1502 assert_select 'a[href=?]', '/projects/ecookbook/repository/revisions/3'
1503 1503 end
1504 1504
1505 1505 def test_show_should_display_watchers
1506 1506 @request.session[:user_id] = 2
1507 1507 Issue.find(1).add_watcher User.find(2)
1508 1508
1509 1509 get :show, :id => 1
1510 1510 assert_select 'div#watchers ul' do
1511 1511 assert_select 'li' do
1512 1512 assert_select 'a[href="/users/2"]'
1513 1513 assert_select 'a[class*=delete]'
1514 1514 end
1515 1515 end
1516 1516 end
1517 1517
1518 1518 def test_show_should_display_watchers_with_gravatars
1519 1519 @request.session[:user_id] = 2
1520 1520 Issue.find(1).add_watcher User.find(2)
1521 1521
1522 1522 with_settings :gravatar_enabled => '1' do
1523 1523 get :show, :id => 1
1524 1524 end
1525 1525
1526 1526 assert_select 'div#watchers ul' do
1527 1527 assert_select 'li' do
1528 1528 assert_select 'img.gravatar'
1529 1529 assert_select 'a[href="/users/2"]'
1530 1530 assert_select 'a[class*=delete]'
1531 1531 end
1532 1532 end
1533 1533 end
1534 1534
1535 1535 def test_show_with_thumbnails_enabled_should_display_thumbnails
1536 1536 @request.session[:user_id] = 2
1537 1537
1538 1538 with_settings :thumbnails_enabled => '1' do
1539 1539 get :show, :id => 14
1540 1540 assert_response :success
1541 1541 end
1542 1542
1543 1543 assert_select 'div.thumbnails' do
1544 1544 assert_select 'a[href="/attachments/16/testfile.png"]' do
1545 1545 assert_select 'img[src="/attachments/thumbnail/16"]'
1546 1546 end
1547 1547 end
1548 1548 end
1549 1549
1550 1550 def test_show_with_thumbnails_disabled_should_not_display_thumbnails
1551 1551 @request.session[:user_id] = 2
1552 1552
1553 1553 with_settings :thumbnails_enabled => '0' do
1554 1554 get :show, :id => 14
1555 1555 assert_response :success
1556 1556 end
1557 1557
1558 1558 assert_select 'div.thumbnails', 0
1559 1559 end
1560 1560
1561 1561 def test_show_with_multi_custom_field
1562 1562 field = CustomField.find(1)
1563 1563 field.update_attribute :multiple, true
1564 1564 issue = Issue.find(1)
1565 1565 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1566 1566 issue.save!
1567 1567
1568 1568 get :show, :id => 1
1569 1569 assert_response :success
1570 1570
1571 1571 assert_select ".cf_1 .value", :text => 'MySQL, Oracle'
1572 1572 end
1573 1573
1574 1574 def test_show_with_multi_user_custom_field
1575 1575 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1576 1576 :tracker_ids => [1], :is_for_all => true)
1577 1577 issue = Issue.find(1)
1578 1578 issue.custom_field_values = {field.id => ['2', '3']}
1579 1579 issue.save!
1580 1580
1581 1581 get :show, :id => 1
1582 1582 assert_response :success
1583 1583
1584 1584 assert_select ".cf_#{field.id} .value", :text => 'Dave Lopper, John Smith' do
1585 1585 assert_select 'a', :text => 'Dave Lopper'
1586 1586 assert_select 'a', :text => 'John Smith'
1587 1587 end
1588 1588 end
1589 1589
1590 1590 def test_show_should_display_private_notes_with_permission_only
1591 1591 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1)
1592 1592 @request.session[:user_id] = 2
1593 1593
1594 1594 get :show, :id => 2
1595 1595 assert_response :success
1596 1596 assert_select "#change-#{journal.id}", 1
1597 1597
1598 1598 Role.find(1).remove_permission! :view_private_notes
1599 1599 get :show, :id => 2
1600 1600 assert_response :success
1601 1601 assert_select "#change-#{journal.id}", 0
1602 1602 end
1603 1603
1604 1604 def test_show_atom
1605 1605 get :show, :id => 2, :format => 'atom'
1606 1606 assert_response :success
1607 1607 assert_equal 'application/atom+xml', response.content_type
1608 1608 # Inline image
1609 1609 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1610 1610 end
1611 1611
1612 1612 def test_show_export_to_pdf
1613 1613 issue = Issue.find(3)
1614 1614 assert issue.relations.select{|r| r.other_issue(issue).visible?}.present?
1615 1615 get :show, :id => 3, :format => 'pdf'
1616 1616 assert_response :success
1617 1617 assert_equal 'application/pdf', @response.content_type
1618 1618 assert @response.body.starts_with?('%PDF')
1619 1619 end
1620 1620
1621 1621 def test_export_to_pdf_with_utf8_u_fffd
1622 1622 # U+FFFD
1623 1623 s = "\xef\xbf\xbd"
1624 1624 s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
1625 1625 issue = Issue.generate!(:subject => s)
1626 1626 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
1627 1627 with_settings :default_language => lang do
1628 1628 get :show, :id => issue.id, :format => 'pdf'
1629 1629 assert_response :success
1630 1630 assert_equal 'application/pdf', @response.content_type
1631 1631 assert @response.body.starts_with?('%PDF')
1632 1632 end
1633 1633 end
1634 1634 end
1635 1635
1636 1636 def test_show_export_to_pdf_with_ancestors
1637 1637 issue = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1638 1638
1639 1639 get :show, :id => issue.id, :format => 'pdf'
1640 1640 assert_response :success
1641 1641 assert_equal 'application/pdf', @response.content_type
1642 1642 assert @response.body.starts_with?('%PDF')
1643 1643 end
1644 1644
1645 1645 def test_show_export_to_pdf_with_descendants
1646 1646 c1 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1647 1647 c2 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1648 1648 c3 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => c1.id)
1649 1649
1650 1650 get :show, :id => 1, :format => 'pdf'
1651 1651 assert_response :success
1652 1652 assert_equal 'application/pdf', @response.content_type
1653 1653 assert @response.body.starts_with?('%PDF')
1654 1654 end
1655 1655
1656 1656 def test_show_export_to_pdf_with_journals
1657 1657 get :show, :id => 1, :format => 'pdf'
1658 1658 assert_response :success
1659 1659 assert_equal 'application/pdf', @response.content_type
1660 1660 assert @response.body.starts_with?('%PDF')
1661 1661 end
1662 1662
1663 1663 def test_show_export_to_pdf_with_changesets
1664 1664 [[100], [100, 101], [100, 101, 102]].each do |cs|
1665 1665 issue1 = Issue.find(3)
1666 1666 issue1.changesets = Changeset.find(cs)
1667 1667 issue1.save!
1668 1668 issue = Issue.find(3)
1669 1669 assert_equal issue.changesets.count, cs.size
1670 1670 get :show, :id => 3, :format => 'pdf'
1671 1671 assert_response :success
1672 1672 assert_equal 'application/pdf', @response.content_type
1673 1673 assert @response.body.starts_with?('%PDF')
1674 1674 end
1675 1675 end
1676 1676
1677 1677 def test_show_invalid_should_respond_with_404
1678 1678 get :show, :id => 999
1679 1679 assert_response 404
1680 1680 end
1681 1681
1682 1682 def test_get_new
1683 1683 @request.session[:user_id] = 2
1684 1684 get :new, :project_id => 1, :tracker_id => 1
1685 1685 assert_response :success
1686 1686
1687 1687 assert_select 'form#issue-form[action=?]', '/projects/ecookbook/issues'
1688 1688 assert_select 'form#issue-form' do
1689 1689 assert_select 'input[name=?]', 'issue[is_private]'
1690 1690 assert_select 'select[name=?]', 'issue[project_id]', 0
1691 1691 assert_select 'select[name=?]', 'issue[tracker_id]'
1692 1692 assert_select 'input[name=?]', 'issue[subject]'
1693 1693 assert_select 'textarea[name=?]', 'issue[description]'
1694 1694 assert_select 'select[name=?]', 'issue[status_id]'
1695 1695 assert_select 'select[name=?]', 'issue[priority_id]'
1696 1696 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1697 1697 assert_select 'select[name=?]', 'issue[category_id]'
1698 1698 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1699 1699 assert_select 'input[name=?]', 'issue[parent_issue_id]'
1700 1700 assert_select 'input[name=?]', 'issue[start_date]'
1701 1701 assert_select 'input[name=?]', 'issue[due_date]'
1702 1702 assert_select 'select[name=?]', 'issue[done_ratio]'
1703 1703 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
1704 1704 assert_select 'input[name=?]', 'issue[watcher_user_ids][]'
1705 1705 end
1706 1706
1707 1707 # Be sure we don't display inactive IssuePriorities
1708 1708 assert ! IssuePriority.find(15).active?
1709 1709 assert_select 'select[name=?]', 'issue[priority_id]' do
1710 1710 assert_select 'option[value="15"]', 0
1711 1711 end
1712 1712 end
1713 1713
1714 1714 def test_get_new_with_minimal_permissions
1715 1715 Role.find(1).update_attribute :permissions, [:add_issues]
1716 1716 WorkflowTransition.where(:role_id => 1).delete_all
1717 1717
1718 1718 @request.session[:user_id] = 2
1719 1719 get :new, :project_id => 1, :tracker_id => 1
1720 1720 assert_response :success
1721 1721
1722 1722 assert_select 'form#issue-form' do
1723 1723 assert_select 'input[name=?]', 'issue[is_private]', 0
1724 1724 assert_select 'select[name=?]', 'issue[project_id]', 0
1725 1725 assert_select 'select[name=?]', 'issue[tracker_id]'
1726 1726 assert_select 'input[name=?]', 'issue[subject]'
1727 1727 assert_select 'textarea[name=?]', 'issue[description]'
1728 1728 assert_select 'select[name=?]', 'issue[status_id]'
1729 1729 assert_select 'select[name=?]', 'issue[priority_id]'
1730 1730 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1731 1731 assert_select 'select[name=?]', 'issue[category_id]'
1732 1732 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1733 1733 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
1734 1734 assert_select 'input[name=?]', 'issue[start_date]'
1735 1735 assert_select 'input[name=?]', 'issue[due_date]'
1736 1736 assert_select 'select[name=?]', 'issue[done_ratio]'
1737 1737 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
1738 1738 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1739 1739 end
1740 1740 end
1741 1741
1742 1742 def test_new_without_project_id
1743 1743 @request.session[:user_id] = 2
1744 1744 get :new
1745 1745 assert_response :success
1746 1746
1747 1747 assert_select 'form#issue-form[action=?]', '/issues'
1748 1748 assert_select 'form#issue-form' do
1749 1749 assert_select 'select[name=?]', 'issue[project_id]'
1750 1750 end
1751 1751 end
1752 1752
1753 1753 def test_new_should_select_default_status
1754 1754 @request.session[:user_id] = 2
1755 1755
1756 1756 get :new, :project_id => 1
1757 1757 assert_response :success
1758 1758 assert_select 'select[name=?]', 'issue[status_id]' do
1759 1759 assert_select 'option[value="1"][selected=selected]'
1760 1760 end
1761 1761 assert_select 'input[name=was_default_status][value="1"]'
1762 1762 end
1763 1763
1764 1764 def test_new_should_propose_allowed_statuses
1765 1765 WorkflowTransition.delete_all
1766 1766 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 1)
1767 1767 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 3)
1768 1768 @request.session[:user_id] = 2
1769 1769
1770 1770 get :new, :project_id => 1
1771 1771 assert_response :success
1772 1772 assert_select 'select[name=?]', 'issue[status_id]' do
1773 1773 assert_select 'option[value="1"]'
1774 1774 assert_select 'option[value="3"]'
1775 1775 assert_select 'option', 2
1776 1776 assert_select 'option[value="1"][selected=selected]'
1777 1777 end
1778 1778 end
1779 1779
1780 1780 def test_new_should_propose_allowed_statuses_without_default_status_allowed
1781 1781 WorkflowTransition.delete_all
1782 1782 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 2)
1783 1783 assert_equal 1, Tracker.find(1).default_status_id
1784 1784 @request.session[:user_id] = 2
1785 1785
1786 1786 get :new, :project_id => 1
1787 1787 assert_response :success
1788 1788 assert_select 'select[name=?]', 'issue[status_id]' do
1789 1789 assert_select 'option[value="2"]'
1790 1790 assert_select 'option', 1
1791 1791 assert_select 'option[value="2"][selected=selected]'
1792 1792 end
1793 1793 end
1794 1794
1795 1795 def test_new_should_propose_allowed_trackers
1796 1796 role = Role.find(1)
1797 1797 role.set_permission_trackers 'add_issues', [1, 3]
1798 1798 role.save!
1799 1799 @request.session[:user_id] = 2
1800 1800
1801 1801 get :new, :project_id => 1
1802 1802 assert_response :success
1803 1803 assert_select 'select[name=?]', 'issue[tracker_id]' do
1804 1804 assert_select 'option', 2
1805 1805 assert_select 'option[value="1"]'
1806 1806 assert_select 'option[value="3"]'
1807 1807 end
1808 1808 end
1809 1809
1810 1810 def test_new_without_allowed_trackers_should_respond_with_403
1811 1811 role = Role.find(1)
1812 1812 role.set_permission_trackers 'add_issues', []
1813 1813 role.save!
1814 1814 @request.session[:user_id] = 2
1815 1815
1816 1816 get :new, :project_id => 1
1817 1817 assert_response 403
1818 1818 end
1819 1819
1820 def test_new_without_projects_should_respond_with_403
1821 Project.delete_all
1822 @request.session[:user_id] = 2
1823
1824 get :new
1825 assert_response 403
1826 assert_select_error /no projects/
1827 end
1828
1829 def test_new_without_enabled_trackers_on_projects_should_respond_with_403
1830 Project.all.each {|p| p.trackers.clear }
1831 @request.session[:user_id] = 2
1832
1833 get :new
1834 assert_response 403
1835 assert_select_error /no projects/
1836 end
1837
1820 1838 def test_new_should_preselect_default_version
1821 1839 version = Version.generate!(:project_id => 1)
1822 1840 Project.find(1).update_attribute :default_version_id, version.id
1823 1841 @request.session[:user_id] = 2
1824 1842
1825 1843 get :new, :project_id => 1
1826 1844 assert_response :success
1827 1845 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
1828 1846 assert_select 'option[value=?][selected=selected]', version.id.to_s
1829 1847 end
1830 1848 end
1831 1849
1832 1850 def test_get_new_with_list_custom_field
1833 1851 @request.session[:user_id] = 2
1834 1852 get :new, :project_id => 1, :tracker_id => 1
1835 1853 assert_response :success
1836 1854
1837 1855 assert_select 'select.list_cf[name=?]', 'issue[custom_field_values][1]' do
1838 1856 assert_select 'option', 4
1839 1857 assert_select 'option[value=MySQL]', :text => 'MySQL'
1840 1858 end
1841 1859 end
1842 1860
1843 1861 def test_get_new_with_multi_custom_field
1844 1862 field = IssueCustomField.find(1)
1845 1863 field.update_attribute :multiple, true
1846 1864
1847 1865 @request.session[:user_id] = 2
1848 1866 get :new, :project_id => 1, :tracker_id => 1
1849 1867 assert_response :success
1850 1868
1851 1869 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
1852 1870 assert_select 'option', 3
1853 1871 assert_select 'option[value=MySQL]', :text => 'MySQL'
1854 1872 end
1855 1873 assert_select 'input[name=?][type=hidden][value=?]', 'issue[custom_field_values][1][]', ''
1856 1874 end
1857 1875
1858 1876 def test_get_new_with_multi_user_custom_field
1859 1877 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1860 1878 :tracker_ids => [1], :is_for_all => true)
1861 1879
1862 1880 @request.session[:user_id] = 2
1863 1881 get :new, :project_id => 1, :tracker_id => 1
1864 1882 assert_response :success
1865 1883
1866 1884 assert_select 'select[name=?][multiple=multiple]', "issue[custom_field_values][#{field.id}][]" do
1867 1885 assert_select 'option', Project.find(1).users.count
1868 1886 assert_select 'option[value="2"]', :text => 'John Smith'
1869 1887 end
1870 1888 assert_select 'input[name=?][type=hidden][value=?]', "issue[custom_field_values][#{field.id}][]", ''
1871 1889 end
1872 1890
1873 1891 def test_get_new_with_date_custom_field
1874 1892 field = IssueCustomField.create!(:name => 'Date', :field_format => 'date', :tracker_ids => [1], :is_for_all => true)
1875 1893
1876 1894 @request.session[:user_id] = 2
1877 1895 get :new, :project_id => 1, :tracker_id => 1
1878 1896 assert_response :success
1879 1897
1880 1898 assert_select 'input[name=?]', "issue[custom_field_values][#{field.id}]"
1881 1899 end
1882 1900
1883 1901 def test_get_new_with_text_custom_field
1884 1902 field = IssueCustomField.create!(:name => 'Text', :field_format => 'text', :tracker_ids => [1], :is_for_all => true)
1885 1903
1886 1904 @request.session[:user_id] = 2
1887 1905 get :new, :project_id => 1, :tracker_id => 1
1888 1906 assert_response :success
1889 1907
1890 1908 assert_select 'textarea[name=?]', "issue[custom_field_values][#{field.id}]"
1891 1909 end
1892 1910
1893 1911 def test_get_new_without_default_start_date_is_creation_date
1894 1912 with_settings :default_issue_start_date_to_creation_date => 0 do
1895 1913 @request.session[:user_id] = 2
1896 1914 get :new, :project_id => 1, :tracker_id => 1
1897 1915 assert_response :success
1898 1916 assert_select 'input[name=?]', 'issue[start_date]'
1899 1917 assert_select 'input[name=?][value]', 'issue[start_date]', 0
1900 1918 end
1901 1919 end
1902 1920
1903 1921 def test_get_new_with_default_start_date_is_creation_date
1904 1922 with_settings :default_issue_start_date_to_creation_date => 1 do
1905 1923 @request.session[:user_id] = 2
1906 1924 get :new, :project_id => 1, :tracker_id => 1
1907 1925 assert_response :success
1908 1926 assert_select 'input[name=?][value=?]', 'issue[start_date]',
1909 1927 Date.today.to_s
1910 1928 end
1911 1929 end
1912 1930
1913 1931 def test_get_new_form_should_allow_attachment_upload
1914 1932 @request.session[:user_id] = 2
1915 1933 get :new, :project_id => 1, :tracker_id => 1
1916 1934 assert_response :success
1917 1935
1918 1936 assert_select 'form[id=issue-form][method=post][enctype="multipart/form-data"]' do
1919 1937 assert_select 'input[name=?][type=file]', 'attachments[dummy][file]'
1920 1938 end
1921 1939 end
1922 1940
1923 1941 def test_get_new_should_prefill_the_form_from_params
1924 1942 @request.session[:user_id] = 2
1925 1943 get :new, :project_id => 1,
1926 1944 :issue => {:tracker_id => 3, :description => 'Prefilled', :custom_field_values => {'2' => 'Custom field value'}}
1927 1945
1928 1946 assert_select 'select[name=?]', 'issue[tracker_id]' do
1929 1947 assert_select 'option[value="3"][selected=selected]'
1930 1948 end
1931 1949 assert_select 'textarea[name=?]', 'issue[description]', :text => /Prefilled/
1932 1950 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Custom field value'
1933 1951 end
1934 1952
1935 1953 def test_get_new_should_mark_required_fields
1936 1954 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1937 1955 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1938 1956 WorkflowPermission.delete_all
1939 1957 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
1940 1958 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
1941 1959 @request.session[:user_id] = 2
1942 1960
1943 1961 get :new, :project_id => 1
1944 1962 assert_response :success
1945 1963
1946 1964 assert_select 'label[for=issue_start_date]' do
1947 1965 assert_select 'span[class=required]', 0
1948 1966 end
1949 1967 assert_select 'label[for=issue_due_date]' do
1950 1968 assert_select 'span[class=required]'
1951 1969 end
1952 1970 assert_select 'label[for=?]', "issue_custom_field_values_#{cf1.id}" do
1953 1971 assert_select 'span[class=required]', 0
1954 1972 end
1955 1973 assert_select 'label[for=?]', "issue_custom_field_values_#{cf2.id}" do
1956 1974 assert_select 'span[class=required]'
1957 1975 end
1958 1976 end
1959 1977
1960 1978 def test_get_new_should_not_display_readonly_fields
1961 1979 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1962 1980 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1963 1981 WorkflowPermission.delete_all
1964 1982 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
1965 1983 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
1966 1984 @request.session[:user_id] = 2
1967 1985
1968 1986 get :new, :project_id => 1
1969 1987 assert_response :success
1970 1988
1971 1989 assert_select 'input[name=?]', 'issue[start_date]'
1972 1990 assert_select 'input[name=?]', 'issue[due_date]', 0
1973 1991 assert_select 'input[name=?]', "issue[custom_field_values][#{cf1.id}]"
1974 1992 assert_select 'input[name=?]', "issue[custom_field_values][#{cf2.id}]", 0
1975 1993 end
1976 1994
1977 1995 def test_new_with_tracker_set_as_readonly_should_accept_status
1978 1996 WorkflowPermission.delete_all
1979 1997 [1, 2].each do |status_id|
1980 1998 WorkflowPermission.create!(:tracker_id => 1, :old_status_id => status_id, :role_id => 1, :field_name => 'tracker_id', :rule => 'readonly')
1981 1999 end
1982 2000 @request.session[:user_id] = 2
1983 2001
1984 2002 get :new, :project_id => 1, :issue => {:status_id => 2}
1985 2003 assert_select 'select[name=?]', 'issue[tracker_id]', 0
1986 2004 assert_select 'select[name=?]', 'issue[status_id]' do
1987 2005 assert_select 'option[value=?][selected=selected]', '2'
1988 2006 end
1989 2007 end
1990 2008
1991 2009 def test_get_new_without_tracker_id
1992 2010 @request.session[:user_id] = 2
1993 2011 get :new, :project_id => 1
1994 2012 assert_response :success
1995 2013
1996 2014 assert_select 'select[name=?]', 'issue[tracker_id]' do
1997 2015 assert_select 'option[value=?][selected=selected]', Project.find(1).trackers.first.id.to_s
1998 2016 end
1999 2017 end
2000 2018
2001 2019 def test_get_new_with_no_default_status_should_display_an_error
2002 2020 @request.session[:user_id] = 2
2003 2021 IssueStatus.delete_all
2004 2022
2005 2023 get :new, :project_id => 1
2006 2024 assert_response 500
2007 2025 assert_select_error /No default issue/
2008 2026 end
2009 2027
2010 2028 def test_get_new_with_no_tracker_should_display_an_error
2011 2029 @request.session[:user_id] = 2
2012 2030 Tracker.delete_all
2013 2031
2014 2032 get :new, :project_id => 1
2015 2033 assert_response 500
2016 2034 assert_select_error /No tracker/
2017 2035 end
2018 2036
2019 2037 def test_new_with_invalid_project_id
2020 2038 @request.session[:user_id] = 1
2021 2039 get :new, :project_id => 'invalid'
2022 2040 assert_response 404
2023 2041 end
2024 2042
2025 2043 def test_new_with_parent_id_should_only_propose_valid_trackers
2026 2044 @request.session[:user_id] = 2
2027 2045 t = Tracker.find(3)
2028 2046 assert !t.disabled_core_fields.include?('parent_issue_id')
2029 2047
2030 2048 get :new, :project_id => 1, issue: { parent_issue_id: 1 }
2031 2049 assert_response :success
2032 2050 assert_select 'option', text: /#{t.name}/, count: 1
2033 2051
2034 2052 t.core_fields = Tracker::CORE_FIELDS - ['parent_issue_id']
2035 2053 t.save!
2036 2054 assert t.disabled_core_fields.include?('parent_issue_id')
2037 2055 get :new, :project_id => 1, issue: { parent_issue_id: 1 }
2038 2056 assert_response :success
2039 2057 assert_select 'option', text: /#{t.name}/, count: 0
2040 2058 end
2041 2059
2042 2060 def test_update_form_for_new_issue
2043 2061 @request.session[:user_id] = 2
2044 2062 xhr :post, :new, :project_id => 1,
2045 2063 :issue => {:tracker_id => 2,
2046 2064 :subject => 'This is the test_new issue',
2047 2065 :description => 'This is the description',
2048 2066 :priority_id => 5}
2049 2067 assert_response :success
2050 2068 assert_equal 'text/javascript', response.content_type
2051 2069 assert_include 'This is the test_new issue', response.body
2052 2070 end
2053 2071
2054 2072 def test_update_form_for_new_issue_should_propose_transitions_based_on_initial_status
2055 2073 @request.session[:user_id] = 2
2056 2074 WorkflowTransition.delete_all
2057 2075 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 2)
2058 2076 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 5)
2059 2077 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4)
2060 2078
2061 2079 post :new, :project_id => 1,
2062 2080 :issue => {:tracker_id => 1,
2063 2081 :status_id => 5,
2064 2082 :subject => 'This is an issue'}
2065 2083
2066 2084 assert_select 'select[name=?]', 'issue[status_id]' do
2067 2085 assert_select 'option[value=?][selected=selected]', '5'
2068 2086 assert_select 'option[value=?]', '2'
2069 2087 assert_select 'option', :count => 2
2070 2088 end
2071 2089 end
2072 2090
2073 2091 def test_update_form_with_default_status_should_ignore_submitted_status_id_if_equals
2074 2092 @request.session[:user_id] = 2
2075 2093 tracker = Tracker.find(2)
2076 2094 tracker.update! :default_status_id => 2
2077 2095 tracker.generate_transitions! 2, 1, :clear => true
2078 2096
2079 2097 post :new, :project_id => 1,
2080 2098 :issue => {:tracker_id => 2,
2081 2099 :status_id => 1},
2082 2100 :was_default_status => 1
2083 2101 assert_response :success
2084 2102
2085 2103 assert_select 'select[name=?]', 'issue[status_id]' do
2086 2104 assert_select 'option[value=?][selected=selected]', '2'
2087 2105 end
2088 2106 end
2089 2107
2090 2108 def test_update_form_for_new_issue_should_ignore_version_when_changing_project
2091 2109 version = Version.generate!(:project_id => 1)
2092 2110 Project.find(1).update_attribute :default_version_id, version.id
2093 2111 @request.session[:user_id] = 2
2094 2112
2095 2113 post :new, :issue => {:project_id => 1,
2096 2114 :fixed_version_id => ''},
2097 2115 :form_update_triggered_by => 'issue_project_id'
2098 2116 assert_response :success
2099 2117
2100 2118 assert_select 'select[name=?]', 'issue[project_id]' do
2101 2119 assert_select 'option[value=?][selected=selected]', '1'
2102 2120 end
2103 2121 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
2104 2122 assert_select 'option[value=?][selected=selected]', version.id.to_s
2105 2123 end
2106 2124 end
2107 2125
2108 2126 def test_post_create
2109 2127 @request.session[:user_id] = 2
2110 2128 assert_difference 'Issue.count' do
2111 2129 assert_no_difference 'Journal.count' do
2112 2130 post :create, :project_id => 1,
2113 2131 :issue => {:tracker_id => 3,
2114 2132 :status_id => 2,
2115 2133 :subject => 'This is the test_new issue',
2116 2134 :description => 'This is the description',
2117 2135 :priority_id => 5,
2118 2136 :start_date => '2010-11-07',
2119 2137 :estimated_hours => '',
2120 2138 :custom_field_values => {'2' => 'Value for field 2'}}
2121 2139 end
2122 2140 end
2123 2141 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2124 2142
2125 2143 issue = Issue.find_by_subject('This is the test_new issue')
2126 2144 assert_not_nil issue
2127 2145 assert_equal 2, issue.author_id
2128 2146 assert_equal 3, issue.tracker_id
2129 2147 assert_equal 2, issue.status_id
2130 2148 assert_equal Date.parse('2010-11-07'), issue.start_date
2131 2149 assert_nil issue.estimated_hours
2132 2150 v = issue.custom_values.where(:custom_field_id => 2).first
2133 2151 assert_not_nil v
2134 2152 assert_equal 'Value for field 2', v.value
2135 2153 end
2136 2154
2137 2155 def test_post_new_with_group_assignment
2138 2156 group = Group.find(11)
2139 2157 project = Project.find(1)
2140 2158 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
2141 2159
2142 2160 with_settings :issue_group_assignment => '1' do
2143 2161 @request.session[:user_id] = 2
2144 2162 assert_difference 'Issue.count' do
2145 2163 post :create, :project_id => project.id,
2146 2164 :issue => {:tracker_id => 3,
2147 2165 :status_id => 1,
2148 2166 :subject => 'This is the test_new_with_group_assignment issue',
2149 2167 :assigned_to_id => group.id}
2150 2168 end
2151 2169 end
2152 2170 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2153 2171
2154 2172 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
2155 2173 assert_not_nil issue
2156 2174 assert_equal group, issue.assigned_to
2157 2175 end
2158 2176
2159 2177 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
2160 2178 with_settings :default_issue_start_date_to_creation_date => 0 do
2161 2179 @request.session[:user_id] = 2
2162 2180 assert_difference 'Issue.count' do
2163 2181 post :create, :project_id => 1,
2164 2182 :issue => {:tracker_id => 3,
2165 2183 :status_id => 2,
2166 2184 :subject => 'This is the test_new issue',
2167 2185 :description => 'This is the description',
2168 2186 :priority_id => 5,
2169 2187 :estimated_hours => '',
2170 2188 :custom_field_values => {'2' => 'Value for field 2'}}
2171 2189 end
2172 2190 assert_redirected_to :controller => 'issues', :action => 'show',
2173 2191 :id => Issue.last.id
2174 2192 issue = Issue.find_by_subject('This is the test_new issue')
2175 2193 assert_not_nil issue
2176 2194 assert_nil issue.start_date
2177 2195 end
2178 2196 end
2179 2197
2180 2198 def test_post_create_without_start_date_and_default_start_date_is_creation_date
2181 2199 with_settings :default_issue_start_date_to_creation_date => 1 do
2182 2200 @request.session[:user_id] = 2
2183 2201 assert_difference 'Issue.count' do
2184 2202 post :create, :project_id => 1,
2185 2203 :issue => {:tracker_id => 3,
2186 2204 :status_id => 2,
2187 2205 :subject => 'This is the test_new issue',
2188 2206 :description => 'This is the description',
2189 2207 :priority_id => 5,
2190 2208 :estimated_hours => '',
2191 2209 :custom_field_values => {'2' => 'Value for field 2'}}
2192 2210 end
2193 2211 assert_redirected_to :controller => 'issues', :action => 'show',
2194 2212 :id => Issue.last.id
2195 2213 issue = Issue.find_by_subject('This is the test_new issue')
2196 2214 assert_not_nil issue
2197 2215 assert_equal Date.today, issue.start_date
2198 2216 end
2199 2217 end
2200 2218
2201 2219 def test_post_create_and_continue
2202 2220 @request.session[:user_id] = 2
2203 2221 assert_difference 'Issue.count' do
2204 2222 post :create, :project_id => 1,
2205 2223 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
2206 2224 :continue => ''
2207 2225 end
2208 2226
2209 2227 issue = Issue.order('id DESC').first
2210 2228 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
2211 2229 assert_not_nil flash[:notice], "flash was not set"
2212 2230 assert_select_in flash[:notice],
2213 2231 'a[href=?][title=?]', "/issues/#{issue.id}", "This is first issue", :text => "##{issue.id}"
2214 2232 end
2215 2233
2216 2234 def test_post_create_without_custom_fields_param
2217 2235 @request.session[:user_id] = 2
2218 2236 assert_difference 'Issue.count' do
2219 2237 post :create, :project_id => 1,
2220 2238 :issue => {:tracker_id => 1,
2221 2239 :subject => 'This is the test_new issue',
2222 2240 :description => 'This is the description',
2223 2241 :priority_id => 5}
2224 2242 end
2225 2243 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2226 2244 end
2227 2245
2228 2246 def test_post_create_with_multi_custom_field
2229 2247 field = IssueCustomField.find_by_name('Database')
2230 2248 field.update_attribute(:multiple, true)
2231 2249
2232 2250 @request.session[:user_id] = 2
2233 2251 assert_difference 'Issue.count' do
2234 2252 post :create, :project_id => 1,
2235 2253 :issue => {:tracker_id => 1,
2236 2254 :subject => 'This is the test_new issue',
2237 2255 :description => 'This is the description',
2238 2256 :priority_id => 5,
2239 2257 :custom_field_values => {'1' => ['', 'MySQL', 'Oracle']}}
2240 2258 end
2241 2259 assert_response 302
2242 2260 issue = Issue.order('id DESC').first
2243 2261 assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
2244 2262 end
2245 2263
2246 2264 def test_post_create_with_empty_multi_custom_field
2247 2265 field = IssueCustomField.find_by_name('Database')
2248 2266 field.update_attribute(:multiple, true)
2249 2267
2250 2268 @request.session[:user_id] = 2
2251 2269 assert_difference 'Issue.count' do
2252 2270 post :create, :project_id => 1,
2253 2271 :issue => {:tracker_id => 1,
2254 2272 :subject => 'This is the test_new issue',
2255 2273 :description => 'This is the description',
2256 2274 :priority_id => 5,
2257 2275 :custom_field_values => {'1' => ['']}}
2258 2276 end
2259 2277 assert_response 302
2260 2278 issue = Issue.order('id DESC').first
2261 2279 assert_equal [''], issue.custom_field_value(1).sort
2262 2280 end
2263 2281
2264 2282 def test_post_create_with_multi_user_custom_field
2265 2283 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
2266 2284 :tracker_ids => [1], :is_for_all => true)
2267 2285
2268 2286 @request.session[:user_id] = 2
2269 2287 assert_difference 'Issue.count' do
2270 2288 post :create, :project_id => 1,
2271 2289 :issue => {:tracker_id => 1,
2272 2290 :subject => 'This is the test_new issue',
2273 2291 :description => 'This is the description',
2274 2292 :priority_id => 5,
2275 2293 :custom_field_values => {field.id.to_s => ['', '2', '3']}}
2276 2294 end
2277 2295 assert_response 302
2278 2296 issue = Issue.order('id DESC').first
2279 2297 assert_equal ['2', '3'], issue.custom_field_value(field).sort
2280 2298 end
2281 2299
2282 2300 def test_post_create_with_required_custom_field_and_without_custom_fields_param
2283 2301 field = IssueCustomField.find_by_name('Database')
2284 2302 field.update_attribute(:is_required, true)
2285 2303
2286 2304 @request.session[:user_id] = 2
2287 2305 assert_no_difference 'Issue.count' do
2288 2306 post :create, :project_id => 1,
2289 2307 :issue => {:tracker_id => 1,
2290 2308 :subject => 'This is the test_new issue',
2291 2309 :description => 'This is the description',
2292 2310 :priority_id => 5}
2293 2311 end
2294 2312 assert_response :success
2295 2313 assert_select_error /Database cannot be blank/
2296 2314 end
2297 2315
2298 2316 def test_create_should_validate_required_fields
2299 2317 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2300 2318 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2301 2319 WorkflowPermission.delete_all
2302 2320 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'required')
2303 2321 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
2304 2322 @request.session[:user_id] = 2
2305 2323
2306 2324 assert_no_difference 'Issue.count' do
2307 2325 post :create, :project_id => 1, :issue => {
2308 2326 :tracker_id => 2,
2309 2327 :status_id => 1,
2310 2328 :subject => 'Test',
2311 2329 :start_date => '',
2312 2330 :due_date => '',
2313 2331 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ''}
2314 2332 }
2315 2333 assert_response :success
2316 2334 end
2317 2335
2318 2336 assert_select_error /Due date cannot be blank/i
2319 2337 assert_select_error /Bar cannot be blank/i
2320 2338 end
2321 2339
2322 2340 def test_create_should_validate_required_list_fields
2323 2341 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'list', :is_for_all => true, :tracker_ids => [1, 2], :multiple => false, :possible_values => ['a', 'b'])
2324 2342 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'list', :is_for_all => true, :tracker_ids => [1, 2], :multiple => true, :possible_values => ['a', 'b'])
2325 2343 WorkflowPermission.delete_all
2326 2344 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf1.id.to_s, :rule => 'required')
2327 2345 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
2328 2346 @request.session[:user_id] = 2
2329 2347
2330 2348 assert_no_difference 'Issue.count' do
2331 2349 post :create, :project_id => 1, :issue => {
2332 2350 :tracker_id => 2,
2333 2351 :status_id => 1,
2334 2352 :subject => 'Test',
2335 2353 :start_date => '',
2336 2354 :due_date => '',
2337 2355 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ['']}
2338 2356 }
2339 2357 assert_response :success
2340 2358 end
2341 2359
2342 2360 assert_select_error /Foo cannot be blank/i
2343 2361 assert_select_error /Bar cannot be blank/i
2344 2362 end
2345 2363
2346 2364 def test_create_should_ignore_readonly_fields
2347 2365 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2348 2366 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2349 2367 WorkflowPermission.delete_all
2350 2368 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
2351 2369 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
2352 2370 @request.session[:user_id] = 2
2353 2371
2354 2372 assert_difference 'Issue.count' do
2355 2373 post :create, :project_id => 1, :issue => {
2356 2374 :tracker_id => 2,
2357 2375 :status_id => 1,
2358 2376 :subject => 'Test',
2359 2377 :start_date => '2012-07-14',
2360 2378 :due_date => '2012-07-16',
2361 2379 :custom_field_values => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}
2362 2380 }
2363 2381 assert_response 302
2364 2382 end
2365 2383
2366 2384 issue = Issue.order('id DESC').first
2367 2385 assert_equal Date.parse('2012-07-14'), issue.start_date
2368 2386 assert_nil issue.due_date
2369 2387 assert_equal 'value1', issue.custom_field_value(cf1)
2370 2388 assert_nil issue.custom_field_value(cf2)
2371 2389 end
2372 2390
2373 2391 def test_create_should_ignore_unallowed_trackers
2374 2392 role = Role.find(1)
2375 2393 role.set_permission_trackers :add_issues, [3]
2376 2394 role.save!
2377 2395 @request.session[:user_id] = 2
2378 2396
2379 2397 issue = new_record(Issue) do
2380 2398 post :create, :project_id => 1, :issue => {
2381 2399 :tracker_id => 1,
2382 2400 :status_id => 1,
2383 2401 :subject => 'Test'
2384 2402 }
2385 2403 assert_response 302
2386 2404 end
2387 2405 assert_equal 3, issue.tracker_id
2388 2406 end
2389 2407
2390 2408 def test_post_create_with_watchers
2391 2409 @request.session[:user_id] = 2
2392 2410 ActionMailer::Base.deliveries.clear
2393 2411
2394 2412 with_settings :notified_events => %w(issue_added) do
2395 2413 assert_difference 'Watcher.count', 2 do
2396 2414 post :create, :project_id => 1,
2397 2415 :issue => {:tracker_id => 1,
2398 2416 :subject => 'This is a new issue with watchers',
2399 2417 :description => 'This is the description',
2400 2418 :priority_id => 5,
2401 2419 :watcher_user_ids => ['2', '3']}
2402 2420 end
2403 2421 end
2404 2422 issue = Issue.find_by_subject('This is a new issue with watchers')
2405 2423 assert_not_nil issue
2406 2424 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2407 2425
2408 2426 # Watchers added
2409 2427 assert_equal [2, 3], issue.watcher_user_ids.sort
2410 2428 assert issue.watched_by?(User.find(3))
2411 2429 # Watchers notified
2412 2430 mail = ActionMailer::Base.deliveries.last
2413 2431 assert_not_nil mail
2414 2432 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
2415 2433 end
2416 2434
2417 2435 def test_post_create_subissue
2418 2436 @request.session[:user_id] = 2
2419 2437
2420 2438 assert_difference 'Issue.count' do
2421 2439 post :create, :project_id => 1,
2422 2440 :issue => {:tracker_id => 1,
2423 2441 :subject => 'This is a child issue',
2424 2442 :parent_issue_id => '2'}
2425 2443 assert_response 302
2426 2444 end
2427 2445 issue = Issue.order('id DESC').first
2428 2446 assert_equal Issue.find(2), issue.parent
2429 2447 end
2430 2448
2431 2449 def test_post_create_subissue_with_sharp_parent_id
2432 2450 @request.session[:user_id] = 2
2433 2451
2434 2452 assert_difference 'Issue.count' do
2435 2453 post :create, :project_id => 1,
2436 2454 :issue => {:tracker_id => 1,
2437 2455 :subject => 'This is a child issue',
2438 2456 :parent_issue_id => '#2'}
2439 2457 assert_response 302
2440 2458 end
2441 2459 issue = Issue.order('id DESC').first
2442 2460 assert_equal Issue.find(2), issue.parent
2443 2461 end
2444 2462
2445 2463 def test_post_create_subissue_with_non_visible_parent_id_should_not_validate
2446 2464 @request.session[:user_id] = 2
2447 2465
2448 2466 assert_no_difference 'Issue.count' do
2449 2467 post :create, :project_id => 1,
2450 2468 :issue => {:tracker_id => 1,
2451 2469 :subject => 'This is a child issue',
2452 2470 :parent_issue_id => '4'}
2453 2471
2454 2472 assert_response :success
2455 2473 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '4'
2456 2474 assert_select_error /Parent task is invalid/i
2457 2475 end
2458 2476 end
2459 2477
2460 2478 def test_post_create_subissue_with_non_numeric_parent_id_should_not_validate
2461 2479 @request.session[:user_id] = 2
2462 2480
2463 2481 assert_no_difference 'Issue.count' do
2464 2482 post :create, :project_id => 1,
2465 2483 :issue => {:tracker_id => 1,
2466 2484 :subject => 'This is a child issue',
2467 2485 :parent_issue_id => '01ABC'}
2468 2486
2469 2487 assert_response :success
2470 2488 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '01ABC'
2471 2489 assert_select_error /Parent task is invalid/i
2472 2490 end
2473 2491 end
2474 2492
2475 2493 def test_post_create_private
2476 2494 @request.session[:user_id] = 2
2477 2495
2478 2496 assert_difference 'Issue.count' do
2479 2497 post :create, :project_id => 1,
2480 2498 :issue => {:tracker_id => 1,
2481 2499 :subject => 'This is a private issue',
2482 2500 :is_private => '1'}
2483 2501 end
2484 2502 issue = Issue.order('id DESC').first
2485 2503 assert issue.is_private?
2486 2504 end
2487 2505
2488 2506 def test_post_create_private_with_set_own_issues_private_permission
2489 2507 role = Role.find(1)
2490 2508 role.remove_permission! :set_issues_private
2491 2509 role.add_permission! :set_own_issues_private
2492 2510
2493 2511 @request.session[:user_id] = 2
2494 2512
2495 2513 assert_difference 'Issue.count' do
2496 2514 post :create, :project_id => 1,
2497 2515 :issue => {:tracker_id => 1,
2498 2516 :subject => 'This is a private issue',
2499 2517 :is_private => '1'}
2500 2518 end
2501 2519 issue = Issue.order('id DESC').first
2502 2520 assert issue.is_private?
2503 2521 end
2504 2522
2505 2523 def test_create_without_project_id
2506 2524 @request.session[:user_id] = 2
2507 2525
2508 2526 assert_difference 'Issue.count' do
2509 2527 post :create,
2510 2528 :issue => {:project_id => 3,
2511 2529 :tracker_id => 2,
2512 2530 :subject => 'Foo'}
2513 2531 assert_response 302
2514 2532 end
2515 2533 issue = Issue.order('id DESC').first
2516 2534 assert_equal 3, issue.project_id
2517 2535 assert_equal 2, issue.tracker_id
2518 2536 end
2519 2537
2520 2538 def test_create_without_project_id_and_continue_should_redirect_without_project_id
2521 2539 @request.session[:user_id] = 2
2522 2540
2523 2541 assert_difference 'Issue.count' do
2524 2542 post :create,
2525 2543 :issue => {:project_id => 3,
2526 2544 :tracker_id => 2,
2527 2545 :subject => 'Foo'},
2528 2546 :continue => '1'
2529 2547 assert_redirected_to '/issues/new?issue%5Bproject_id%5D=3&issue%5Btracker_id%5D=2'
2530 2548 end
2531 2549 end
2532 2550
2533 2551 def test_create_without_project_id_should_be_denied_without_permission
2534 2552 Role.non_member.remove_permission! :add_issues
2535 2553 Role.anonymous.remove_permission! :add_issues
2536 2554 @request.session[:user_id] = 2
2537 2555
2538 2556 assert_no_difference 'Issue.count' do
2539 2557 post :create,
2540 2558 :issue => {:project_id => 3,
2541 2559 :tracker_id => 2,
2542 2560 :subject => 'Foo'}
2543 assert_response 422
2561 assert_response 403
2544 2562 end
2545 2563 end
2546 2564
2547 2565 def test_create_without_project_id_with_failure_should_not_set_project
2548 2566 @request.session[:user_id] = 2
2549 2567
2550 2568 post :create,
2551 2569 :issue => {:project_id => 3,
2552 2570 :tracker_id => 2,
2553 2571 :subject => ''}
2554 2572 assert_response :success
2555 2573 # no project menu
2556 2574 assert_select '#main-menu', 0
2557 2575 end
2558 2576
2559 2577 def test_post_create_should_send_a_notification
2560 2578 ActionMailer::Base.deliveries.clear
2561 2579 @request.session[:user_id] = 2
2562 2580 with_settings :notified_events => %w(issue_added) do
2563 2581 assert_difference 'Issue.count' do
2564 2582 post :create, :project_id => 1,
2565 2583 :issue => {:tracker_id => 3,
2566 2584 :subject => 'This is the test_new issue',
2567 2585 :description => 'This is the description',
2568 2586 :priority_id => 5,
2569 2587 :estimated_hours => '',
2570 2588 :custom_field_values => {'2' => 'Value for field 2'}}
2571 2589 end
2572 2590 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2573 2591
2574 2592 assert_equal 1, ActionMailer::Base.deliveries.size
2575 2593 end
2576 2594 end
2577 2595
2578 2596 def test_post_create_should_preserve_fields_values_on_validation_failure
2579 2597 @request.session[:user_id] = 2
2580 2598 post :create, :project_id => 1,
2581 2599 :issue => {:tracker_id => 1,
2582 2600 # empty subject
2583 2601 :subject => '',
2584 2602 :description => 'This is a description',
2585 2603 :priority_id => 6,
2586 2604 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
2587 2605 assert_response :success
2588 2606
2589 2607 assert_select 'textarea[name=?]', 'issue[description]', :text => 'This is a description'
2590 2608 assert_select 'select[name=?]', 'issue[priority_id]' do
2591 2609 assert_select 'option[value="6"][selected=selected]', :text => 'High'
2592 2610 end
2593 2611 # Custom fields
2594 2612 assert_select 'select[name=?]', 'issue[custom_field_values][1]' do
2595 2613 assert_select 'option[value=Oracle][selected=selected]', :text => 'Oracle'
2596 2614 end
2597 2615 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Value for field 2'
2598 2616 end
2599 2617
2600 2618 def test_post_create_with_failure_should_preserve_watchers
2601 2619 assert !User.find(8).member_of?(Project.find(1))
2602 2620
2603 2621 @request.session[:user_id] = 2
2604 2622 post :create, :project_id => 1,
2605 2623 :issue => {:tracker_id => 1,
2606 2624 :watcher_user_ids => ['3', '8']}
2607 2625 assert_response :success
2608 2626
2609 2627 assert_select 'input[name=?][value="2"]:not(checked)', 'issue[watcher_user_ids][]'
2610 2628 assert_select 'input[name=?][value="3"][checked=checked]', 'issue[watcher_user_ids][]'
2611 2629 assert_select 'input[name=?][value="8"][checked=checked]', 'issue[watcher_user_ids][]'
2612 2630 end
2613 2631
2614 2632 def test_post_create_should_ignore_non_safe_attributes
2615 2633 @request.session[:user_id] = 2
2616 2634 assert_nothing_raised do
2617 2635 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
2618 2636 end
2619 2637 end
2620 2638
2621 2639 def test_post_create_with_attachment
2622 2640 set_tmp_attachments_directory
2623 2641 @request.session[:user_id] = 2
2624 2642
2625 2643 assert_difference 'Issue.count' do
2626 2644 assert_difference 'Attachment.count' do
2627 2645 assert_no_difference 'Journal.count' do
2628 2646 post :create, :project_id => 1,
2629 2647 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2630 2648 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2631 2649 end
2632 2650 end
2633 2651 end
2634 2652
2635 2653 issue = Issue.order('id DESC').first
2636 2654 attachment = Attachment.order('id DESC').first
2637 2655
2638 2656 assert_equal issue, attachment.container
2639 2657 assert_equal 2, attachment.author_id
2640 2658 assert_equal 'testfile.txt', attachment.filename
2641 2659 assert_equal 'text/plain', attachment.content_type
2642 2660 assert_equal 'test file', attachment.description
2643 2661 assert_equal 59, attachment.filesize
2644 2662 assert File.exists?(attachment.diskfile)
2645 2663 assert_equal 59, File.size(attachment.diskfile)
2646 2664 end
2647 2665
2648 2666 def test_post_create_with_attachment_should_notify_with_attachments
2649 2667 ActionMailer::Base.deliveries.clear
2650 2668 set_tmp_attachments_directory
2651 2669 @request.session[:user_id] = 2
2652 2670
2653 2671 with_settings :notified_events => %w(issue_added) do
2654 2672 assert_difference 'Issue.count' do
2655 2673 post :create, :project_id => 1,
2656 2674 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2657 2675 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2658 2676 end
2659 2677 end
2660 2678
2661 2679 assert_not_nil ActionMailer::Base.deliveries.last
2662 2680 assert_select_email do
2663 2681 assert_select 'a[href^=?]', 'http://localhost:3000/attachments/download', 'testfile.txt'
2664 2682 end
2665 2683 end
2666 2684
2667 2685 def test_post_create_with_failure_should_save_attachments
2668 2686 set_tmp_attachments_directory
2669 2687 @request.session[:user_id] = 2
2670 2688
2671 2689 assert_no_difference 'Issue.count' do
2672 2690 assert_difference 'Attachment.count' do
2673 2691 post :create, :project_id => 1,
2674 2692 :issue => { :tracker_id => '1', :subject => '' },
2675 2693 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2676 2694 assert_response :success
2677 2695 end
2678 2696 end
2679 2697
2680 2698 attachment = Attachment.order('id DESC').first
2681 2699 assert_equal 'testfile.txt', attachment.filename
2682 2700 assert File.exists?(attachment.diskfile)
2683 2701 assert_nil attachment.container
2684 2702
2685 2703 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
2686 2704 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
2687 2705 end
2688 2706
2689 2707 def test_post_create_with_failure_should_keep_saved_attachments
2690 2708 set_tmp_attachments_directory
2691 2709 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2692 2710 @request.session[:user_id] = 2
2693 2711
2694 2712 assert_no_difference 'Issue.count' do
2695 2713 assert_no_difference 'Attachment.count' do
2696 2714 post :create, :project_id => 1,
2697 2715 :issue => { :tracker_id => '1', :subject => '' },
2698 2716 :attachments => {'p0' => {'token' => attachment.token}}
2699 2717 assert_response :success
2700 2718 end
2701 2719 end
2702 2720
2703 2721 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
2704 2722 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
2705 2723 end
2706 2724
2707 2725 def test_post_create_should_attach_saved_attachments
2708 2726 set_tmp_attachments_directory
2709 2727 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2710 2728 @request.session[:user_id] = 2
2711 2729
2712 2730 assert_difference 'Issue.count' do
2713 2731 assert_no_difference 'Attachment.count' do
2714 2732 post :create, :project_id => 1,
2715 2733 :issue => { :tracker_id => '1', :subject => 'Saved attachments' },
2716 2734 :attachments => {'p0' => {'token' => attachment.token}}
2717 2735 assert_response 302
2718 2736 end
2719 2737 end
2720 2738
2721 2739 issue = Issue.order('id DESC').first
2722 2740 assert_equal 1, issue.attachments.count
2723 2741
2724 2742 attachment.reload
2725 2743 assert_equal issue, attachment.container
2726 2744 end
2727 2745
2728 2746 def setup_without_workflow_privilege
2729 2747 WorkflowTransition.where(["role_id = ?", Role.anonymous.id]).delete_all
2730 2748 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2731 2749 end
2732 2750 private :setup_without_workflow_privilege
2733 2751
2734 2752 test "without workflow privilege #new should propose default status only" do
2735 2753 setup_without_workflow_privilege
2736 2754 get :new, :project_id => 1
2737 2755 assert_response :success
2738 2756
2739 2757 assert_select 'select[name=?]', 'issue[status_id]' do
2740 2758 assert_select 'option', 1
2741 2759 assert_select 'option[value=?][selected=selected]', '1'
2742 2760 end
2743 2761 end
2744 2762
2745 2763 test "without workflow privilege #create should accept default status" do
2746 2764 setup_without_workflow_privilege
2747 2765 assert_difference 'Issue.count' do
2748 2766 post :create, :project_id => 1,
2749 2767 :issue => {:tracker_id => 1,
2750 2768 :subject => 'This is an issue',
2751 2769 :status_id => 1}
2752 2770 end
2753 2771 issue = Issue.order('id').last
2754 2772 assert_not_nil issue.default_status
2755 2773 assert_equal issue.default_status, issue.status
2756 2774 end
2757 2775
2758 2776 test "without workflow privilege #create should ignore unauthorized status" do
2759 2777 setup_without_workflow_privilege
2760 2778 assert_difference 'Issue.count' do
2761 2779 post :create, :project_id => 1,
2762 2780 :issue => {:tracker_id => 1,
2763 2781 :subject => 'This is an issue',
2764 2782 :status_id => 3}
2765 2783 end
2766 2784 issue = Issue.order('id').last
2767 2785 assert_not_nil issue.default_status
2768 2786 assert_equal issue.default_status, issue.status
2769 2787 end
2770 2788
2771 2789 test "without workflow privilege #update should ignore status change" do
2772 2790 setup_without_workflow_privilege
2773 2791 assert_difference 'Journal.count' do
2774 2792 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2775 2793 end
2776 2794 assert_equal 1, Issue.find(1).status_id
2777 2795 end
2778 2796
2779 2797 test "without workflow privilege #update ignore attributes changes" do
2780 2798 setup_without_workflow_privilege
2781 2799 assert_difference 'Journal.count' do
2782 2800 put :update, :id => 1,
2783 2801 :issue => {:subject => 'changed', :assigned_to_id => 2,
2784 2802 :notes => 'just trying'}
2785 2803 end
2786 2804 issue = Issue.find(1)
2787 2805 assert_equal "Cannot print recipes", issue.subject
2788 2806 assert_nil issue.assigned_to
2789 2807 end
2790 2808
2791 2809 def setup_with_workflow_privilege
2792 2810 WorkflowTransition.where(["role_id = ?", Role.anonymous.id]).delete_all
2793 2811 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
2794 2812 :old_status_id => 1, :new_status_id => 3)
2795 2813 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
2796 2814 :old_status_id => 1, :new_status_id => 4)
2797 2815 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2798 2816 end
2799 2817 private :setup_with_workflow_privilege
2800 2818
2801 2819 def setup_with_workflow_privilege_and_edit_issues_permission
2802 2820 setup_with_workflow_privilege
2803 2821 Role.anonymous.add_permission! :add_issues, :edit_issues
2804 2822 end
2805 2823 private :setup_with_workflow_privilege_and_edit_issues_permission
2806 2824
2807 2825 test "with workflow privilege and :edit_issues permission should accept authorized status" do
2808 2826 setup_with_workflow_privilege_and_edit_issues_permission
2809 2827 assert_difference 'Journal.count' do
2810 2828 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2811 2829 end
2812 2830 assert_equal 3, Issue.find(1).status_id
2813 2831 end
2814 2832
2815 2833 test "with workflow privilege and :edit_issues permission should ignore unauthorized status" do
2816 2834 setup_with_workflow_privilege_and_edit_issues_permission
2817 2835 assert_difference 'Journal.count' do
2818 2836 put :update, :id => 1, :issue => {:status_id => 2, :notes => 'just trying'}
2819 2837 end
2820 2838 assert_equal 1, Issue.find(1).status_id
2821 2839 end
2822 2840
2823 2841 test "with workflow privilege and :edit_issues permission should accept authorized attributes changes" do
2824 2842 setup_with_workflow_privilege_and_edit_issues_permission
2825 2843 assert_difference 'Journal.count' do
2826 2844 put :update, :id => 1,
2827 2845 :issue => {:subject => 'changed', :assigned_to_id => 2,
2828 2846 :notes => 'just trying'}
2829 2847 end
2830 2848 issue = Issue.find(1)
2831 2849 assert_equal "changed", issue.subject
2832 2850 assert_equal 2, issue.assigned_to_id
2833 2851 end
2834 2852
2835 2853 def test_new_as_copy
2836 2854 orig = Issue.find(1)
2837 2855 @request.session[:user_id] = 2
2838 2856
2839 2857 get :new, :project_id => 1, :copy_from => orig.id
2840 2858 assert_response :success
2841 2859
2842 2860 assert_select 'form[id=issue-form][action="/projects/ecookbook/issues"]' do
2843 2861 assert_select 'select[name=?]', 'issue[project_id]' do
2844 2862 assert_select 'option[value="1"][selected=selected]', :text => 'eCookbook'
2845 2863 assert_select 'option[value="2"]:not([selected])', :text => 'OnlineStore'
2846 2864 end
2847 2865 assert_select 'input[name=?][value=?]', 'issue[subject]', orig.subject
2848 2866 assert_select 'input[name=copy_from][value="1"]'
2849 2867 end
2850 2868 end
2851 2869
2852 2870 def test_new_as_copy_without_add_issues_permission_should_not_propose_current_project_as_target
2853 2871 user = setup_user_with_copy_but_not_add_permission
2854 2872
2855 2873 @request.session[:user_id] = user.id
2856 2874 get :new, :project_id => 1, :copy_from => 1
2857 2875 assert_response :success
2858 2876
2859 2877 assert_select 'select[name=?]', 'issue[project_id]' do
2860 2878 assert_select 'option[value="1"]', 0
2861 2879 assert_select 'option[value="2"]', :text => 'OnlineStore'
2862 2880 end
2863 2881 end
2864 2882
2865 2883 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
2866 2884 @request.session[:user_id] = 2
2867 2885 issue = Issue.find(3)
2868 2886 assert issue.attachments.count > 0
2869 2887 get :new, :project_id => 1, :copy_from => 3
2870 2888
2871 2889 assert_select 'input[name=copy_attachments][type=checkbox][checked=checked][value="1"]'
2872 2890 end
2873 2891
2874 2892 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
2875 2893 @request.session[:user_id] = 2
2876 2894 issue = Issue.find(3)
2877 2895 issue.attachments.delete_all
2878 2896 get :new, :project_id => 1, :copy_from => 3
2879 2897
2880 2898 assert_select 'input[name=copy_attachments]', 0
2881 2899 end
2882 2900
2883 2901 def test_new_as_copy_should_preserve_parent_id
2884 2902 @request.session[:user_id] = 2
2885 2903 issue = Issue.generate!(:parent_issue_id => 2)
2886 2904 get :new, :project_id => 1, :copy_from => issue.id
2887 2905
2888 2906 assert_select 'input[name=?][value="2"]', 'issue[parent_issue_id]'
2889 2907 end
2890 2908
2891 2909 def test_new_as_copy_with_subtasks_should_show_copy_subtasks_checkbox
2892 2910 @request.session[:user_id] = 2
2893 2911 issue = Issue.generate_with_descendants!
2894 2912 get :new, :project_id => 1, :copy_from => issue.id
2895 2913
2896 2914 assert_select 'input[type=checkbox][name=copy_subtasks][checked=checked][value="1"]'
2897 2915 end
2898 2916
2899 2917 def test_new_as_copy_with_invalid_issue_should_respond_with_404
2900 2918 @request.session[:user_id] = 2
2901 2919 get :new, :project_id => 1, :copy_from => 99999
2902 2920 assert_response 404
2903 2921 end
2904 2922
2905 2923 def test_create_as_copy_on_different_project
2906 2924 @request.session[:user_id] = 2
2907 2925 assert_difference 'Issue.count' do
2908 2926 post :create, :project_id => 1, :copy_from => 1,
2909 2927 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2910 2928 end
2911 2929 issue = Issue.order('id DESC').first
2912 2930 assert_redirected_to "/issues/#{issue.id}"
2913 2931
2914 2932 assert_equal 2, issue.project_id
2915 2933 assert_equal 3, issue.tracker_id
2916 2934 assert_equal 'Copy', issue.subject
2917 2935 end
2918 2936
2919 2937 def test_create_as_copy_should_allow_status_to_be_set_to_default
2920 2938 copied = Issue.generate! :status_id => 2
2921 2939 assert_equal 2, copied.reload.status_id
2922 2940
2923 2941 @request.session[:user_id] = 2
2924 2942 assert_difference 'Issue.count' do
2925 2943 post :create, :project_id => 1, :copy_from => copied.id,
2926 2944 :issue => {:project_id => '1', :tracker_id => '1', :status_id => '1'},
2927 2945 :was_default_status => '1'
2928 2946 end
2929 2947 issue = Issue.order('id DESC').first
2930 2948 assert_equal 1, issue.status_id
2931 2949 end
2932 2950
2933 2951 def test_create_as_copy_should_copy_attachments
2934 2952 @request.session[:user_id] = 2
2935 2953 issue = Issue.find(3)
2936 2954 count = issue.attachments.count
2937 2955 assert count > 0
2938 2956 assert_difference 'Issue.count' do
2939 2957 assert_difference 'Attachment.count', count do
2940 2958 post :create, :project_id => 1, :copy_from => 3,
2941 2959 :issue => {:project_id => '1', :tracker_id => '3',
2942 2960 :status_id => '1', :subject => 'Copy with attachments'},
2943 2961 :copy_attachments => '1'
2944 2962 end
2945 2963 end
2946 2964 copy = Issue.order('id DESC').first
2947 2965 assert_equal count, copy.attachments.count
2948 2966 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
2949 2967 end
2950 2968
2951 2969 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
2952 2970 @request.session[:user_id] = 2
2953 2971 issue = Issue.find(3)
2954 2972 count = issue.attachments.count
2955 2973 assert count > 0
2956 2974 assert_difference 'Issue.count' do
2957 2975 assert_no_difference 'Attachment.count' do
2958 2976 post :create, :project_id => 1, :copy_from => 3,
2959 2977 :issue => {:project_id => '1', :tracker_id => '3',
2960 2978 :status_id => '1', :subject => 'Copy with attachments'}
2961 2979 end
2962 2980 end
2963 2981 copy = Issue.order('id DESC').first
2964 2982 assert_equal 0, copy.attachments.count
2965 2983 end
2966 2984
2967 2985 def test_create_as_copy_with_attachments_should_also_add_new_files
2968 2986 @request.session[:user_id] = 2
2969 2987 issue = Issue.find(3)
2970 2988 count = issue.attachments.count
2971 2989 assert count > 0
2972 2990 assert_difference 'Issue.count' do
2973 2991 assert_difference 'Attachment.count', count + 1 do
2974 2992 post :create, :project_id => 1, :copy_from => 3,
2975 2993 :issue => {:project_id => '1', :tracker_id => '3',
2976 2994 :status_id => '1', :subject => 'Copy with attachments'},
2977 2995 :copy_attachments => '1',
2978 2996 :attachments => {'1' =>
2979 2997 {'file' => uploaded_test_file('testfile.txt', 'text/plain'),
2980 2998 'description' => 'test file'}}
2981 2999 end
2982 3000 end
2983 3001 copy = Issue.order('id DESC').first
2984 3002 assert_equal count + 1, copy.attachments.count
2985 3003 end
2986 3004
2987 3005 def test_create_as_copy_should_add_relation_with_copied_issue
2988 3006 @request.session[:user_id] = 2
2989 3007 assert_difference 'Issue.count' do
2990 3008 assert_difference 'IssueRelation.count' do
2991 3009 post :create, :project_id => 1, :copy_from => 1, :link_copy => '1',
2992 3010 :issue => {:project_id => '1', :tracker_id => '3',
2993 3011 :status_id => '1', :subject => 'Copy'}
2994 3012 end
2995 3013 end
2996 3014 copy = Issue.order('id DESC').first
2997 3015 assert_equal 1, copy.relations.size
2998 3016 end
2999 3017
3000 3018 def test_create_as_copy_should_allow_not_to_add_relation_with_copied_issue
3001 3019 @request.session[:user_id] = 2
3002 3020 assert_difference 'Issue.count' do
3003 3021 assert_no_difference 'IssueRelation.count' do
3004 3022 post :create, :project_id => 1, :copy_from => 1,
3005 3023 :issue => {:subject => 'Copy'}
3006 3024 end
3007 3025 end
3008 3026 end
3009 3027
3010 3028 def test_create_as_copy_should_always_add_relation_with_copied_issue_by_setting
3011 3029 with_settings :link_copied_issue => 'yes' do
3012 3030 @request.session[:user_id] = 2
3013 3031 assert_difference 'Issue.count' do
3014 3032 assert_difference 'IssueRelation.count' do
3015 3033 post :create, :project_id => 1, :copy_from => 1,
3016 3034 :issue => {:subject => 'Copy'}
3017 3035 end
3018 3036 end
3019 3037 end
3020 3038 end
3021 3039
3022 3040 def test_create_as_copy_should_never_add_relation_with_copied_issue_by_setting
3023 3041 with_settings :link_copied_issue => 'no' do
3024 3042 @request.session[:user_id] = 2
3025 3043 assert_difference 'Issue.count' do
3026 3044 assert_no_difference 'IssueRelation.count' do
3027 3045 post :create, :project_id => 1, :copy_from => 1, :link_copy => '1',
3028 3046 :issue => {:subject => 'Copy'}
3029 3047 end
3030 3048 end
3031 3049 end
3032 3050 end
3033 3051
3034 3052 def test_create_as_copy_should_copy_subtasks
3035 3053 @request.session[:user_id] = 2
3036 3054 issue = Issue.generate_with_descendants!
3037 3055 count = issue.descendants.count
3038 3056 assert_difference 'Issue.count', count + 1 do
3039 3057 post :create, :project_id => 1, :copy_from => issue.id,
3040 3058 :issue => {:project_id => '1', :tracker_id => '3',
3041 3059 :status_id => '1', :subject => 'Copy with subtasks'},
3042 3060 :copy_subtasks => '1'
3043 3061 end
3044 3062 copy = Issue.where(:parent_id => nil).order('id DESC').first
3045 3063 assert_equal count, copy.descendants.count
3046 3064 assert_equal issue.descendants.map(&:subject).sort, copy.descendants.map(&:subject).sort
3047 3065 end
3048 3066
3049 3067 def test_create_as_copy_to_a_different_project_should_copy_subtask_custom_fields
3050 3068 issue = Issue.generate! {|i| i.custom_field_values = {'2' => 'Foo'}}
3051 3069 child = Issue.generate!(:parent_issue_id => issue.id) {|i| i.custom_field_values = {'2' => 'Bar'}}
3052 3070 @request.session[:user_id] = 1
3053 3071
3054 3072 assert_difference 'Issue.count', 2 do
3055 3073 post :create, :project_id => 'ecookbook', :copy_from => issue.id,
3056 3074 :issue => {:project_id => '2', :tracker_id => 1, :status_id => '1',
3057 3075 :subject => 'Copy with subtasks', :custom_field_values => {'2' => 'Foo'}},
3058 3076 :copy_subtasks => '1'
3059 3077 end
3060 3078
3061 3079 child_copy, issue_copy = Issue.order(:id => :desc).limit(2).to_a
3062 3080 assert_equal 2, issue_copy.project_id
3063 3081 assert_equal 'Foo', issue_copy.custom_field_value(2)
3064 3082 assert_equal 'Bar', child_copy.custom_field_value(2)
3065 3083 end
3066 3084
3067 3085 def test_create_as_copy_without_copy_subtasks_option_should_not_copy_subtasks
3068 3086 @request.session[:user_id] = 2
3069 3087 issue = Issue.generate_with_descendants!
3070 3088 assert_difference 'Issue.count', 1 do
3071 3089 post :create, :project_id => 1, :copy_from => 3,
3072 3090 :issue => {:project_id => '1', :tracker_id => '3',
3073 3091 :status_id => '1', :subject => 'Copy with subtasks'}
3074 3092 end
3075 3093 copy = Issue.where(:parent_id => nil).order('id DESC').first
3076 3094 assert_equal 0, copy.descendants.count
3077 3095 end
3078 3096
3079 3097 def test_create_as_copy_with_failure
3080 3098 @request.session[:user_id] = 2
3081 3099 post :create, :project_id => 1, :copy_from => 1,
3082 3100 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
3083 3101
3084 3102 assert_response :success
3085 3103
3086 3104 assert_select 'form#issue-form[action="/projects/ecookbook/issues"]' do
3087 3105 assert_select 'select[name=?]', 'issue[project_id]' do
3088 3106 assert_select 'option[value="1"]:not([selected])', :text => 'eCookbook'
3089 3107 assert_select 'option[value="2"][selected=selected]', :text => 'OnlineStore'
3090 3108 end
3091 3109 assert_select 'input[name=copy_from][value="1"]'
3092 3110 end
3093 3111 end
3094 3112
3095 3113 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
3096 3114 @request.session[:user_id] = 2
3097 3115 assert !User.find(2).member_of?(Project.find(4))
3098 3116
3099 3117 assert_difference 'Issue.count' do
3100 3118 post :create, :project_id => 1, :copy_from => 1,
3101 3119 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
3102 3120 end
3103 3121 issue = Issue.order('id DESC').first
3104 3122 assert_equal 1, issue.project_id
3105 3123 end
3106 3124
3107 3125 def test_get_edit
3108 3126 @request.session[:user_id] = 2
3109 3127 get :edit, :id => 1
3110 3128 assert_response :success
3111 3129
3112 3130 # Be sure we don't display inactive IssuePriorities
3113 3131 assert ! IssuePriority.find(15).active?
3114 3132 assert_select 'select[name=?]', 'issue[priority_id]' do
3115 3133 assert_select 'option[value="15"]', 0
3116 3134 end
3117 3135 end
3118 3136
3119 3137 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
3120 3138 @request.session[:user_id] = 2
3121 3139 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
3122 3140
3123 3141 get :edit, :id => 1
3124 3142 assert_select 'input[name=?]', 'time_entry[hours]'
3125 3143 end
3126 3144
3127 3145 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
3128 3146 @request.session[:user_id] = 2
3129 3147 Role.find_by_name('Manager').remove_permission! :log_time
3130 3148
3131 3149 get :edit, :id => 1
3132 3150 assert_select 'input[name=?]', 'time_entry[hours]', 0
3133 3151 end
3134 3152
3135 3153 def test_get_edit_with_params
3136 3154 @request.session[:user_id] = 2
3137 3155 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
3138 3156 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => 10 }
3139 3157 assert_response :success
3140 3158
3141 3159 assert_select 'select[name=?]', 'issue[status_id]' do
3142 3160 assert_select 'option[value="5"][selected=selected]', :text => 'Closed'
3143 3161 end
3144 3162
3145 3163 assert_select 'select[name=?]', 'issue[priority_id]' do
3146 3164 assert_select 'option[value="7"][selected=selected]', :text => 'Urgent'
3147 3165 end
3148 3166
3149 3167 assert_select 'input[name=?][value="2.5"]', 'time_entry[hours]'
3150 3168 assert_select 'select[name=?]', 'time_entry[activity_id]' do
3151 3169 assert_select 'option[value="10"][selected=selected]', :text => 'Development'
3152 3170 end
3153 3171 assert_select 'input[name=?][value=test_get_edit_with_params]', 'time_entry[comments]'
3154 3172 end
3155 3173
3156 3174 def test_get_edit_with_multi_custom_field
3157 3175 field = CustomField.find(1)
3158 3176 field.update_attribute :multiple, true
3159 3177 issue = Issue.find(1)
3160 3178 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
3161 3179 issue.save!
3162 3180
3163 3181 @request.session[:user_id] = 2
3164 3182 get :edit, :id => 1
3165 3183 assert_response :success
3166 3184
3167 3185 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
3168 3186 assert_select 'option', 3
3169 3187 assert_select 'option[value=MySQL][selected=selected]'
3170 3188 assert_select 'option[value=Oracle][selected=selected]'
3171 3189 assert_select 'option[value=PostgreSQL]:not([selected])'
3172 3190 end
3173 3191 end
3174 3192
3175 3193 def test_update_form_for_existing_issue
3176 3194 @request.session[:user_id] = 2
3177 3195 xhr :patch, :edit, :id => 1,
3178 3196 :issue => {:tracker_id => 2,
3179 3197 :subject => 'This is the test_new issue',
3180 3198 :description => 'This is the description',
3181 3199 :priority_id => 5}
3182 3200 assert_response :success
3183 3201 assert_equal 'text/javascript', response.content_type
3184 3202
3185 3203 assert_include 'This is the test_new issue', response.body
3186 3204 end
3187 3205
3188 3206 def test_update_form_for_existing_issue_should_keep_issue_author
3189 3207 @request.session[:user_id] = 3
3190 3208 patch :edit, :id => 1, :issue => {:subject => 'Changed'}
3191 3209 assert_response :success
3192 3210
3193 3211 assert_equal User.find(2), Issue.find(1).author
3194 3212 end
3195 3213
3196 3214 def test_update_form_for_existing_issue_should_propose_transitions_based_on_initial_status
3197 3215 @request.session[:user_id] = 2
3198 3216 WorkflowTransition.delete_all
3199 3217 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
3200 3218 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
3201 3219 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 5, :new_status_id => 4)
3202 3220
3203 3221 patch :edit, :id => 2,
3204 3222 :issue => {:tracker_id => 2,
3205 3223 :status_id => 5,
3206 3224 :subject => 'This is an issue'}
3207 3225
3208 3226 assert_select 'select[name=?]', 'issue[status_id]' do
3209 3227 assert_select 'option[value="1"]'
3210 3228 assert_select 'option[value="2"]'
3211 3229 assert_select 'option[value="5"][selected=selected]'
3212 3230 assert_select 'option', 3
3213 3231 end
3214 3232 end
3215 3233
3216 3234 def test_update_form_for_existing_issue_with_project_change
3217 3235 @request.session[:user_id] = 2
3218 3236 patch :edit, :id => 1,
3219 3237 :issue => {:project_id => 2,
3220 3238 :tracker_id => 2,
3221 3239 :subject => 'This is the test_new issue',
3222 3240 :description => 'This is the description',
3223 3241 :priority_id => 5}
3224 3242 assert_response :success
3225 3243 assert_select 'select[name=?]', 'issue[project_id]' do
3226 3244 assert_select 'option[value="2"][selected=selected]'
3227 3245 end
3228 3246 assert_select 'select[name=?]', 'issue[tracker_id]' do
3229 3247 assert_select 'option[value="2"][selected=selected]'
3230 3248 end
3231 3249 assert_select 'input[name=?][value=?]', 'issue[subject]', 'This is the test_new issue'
3232 3250 end
3233 3251
3234 3252 def test_update_form_should_keep_category_with_same_when_changing_project
3235 3253 source = Project.generate!
3236 3254 target = Project.generate!
3237 3255 source_category = IssueCategory.create!(:name => 'Foo', :project => source)
3238 3256 target_category = IssueCategory.create!(:name => 'Foo', :project => target)
3239 3257 issue = Issue.generate!(:project => source, :category => source_category)
3240 3258
3241 3259 @request.session[:user_id] = 1
3242 3260 patch :edit, :id => issue.id,
3243 3261 :issue => {:project_id => target.id, :category_id => source_category.id}
3244 3262 assert_response :success
3245 3263
3246 3264 assert_select 'select[name=?]', 'issue[category_id]' do
3247 3265 assert_select 'option[value=?][selected=selected]', target_category.id.to_s
3248 3266 end
3249 3267 end
3250 3268
3251 3269 def test_update_form_should_propose_default_status_for_existing_issue
3252 3270 @request.session[:user_id] = 2
3253 3271 WorkflowTransition.delete_all
3254 3272 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3)
3255 3273
3256 3274 patch :edit, :id => 2
3257 3275 assert_response :success
3258 3276 assert_select 'select[name=?]', 'issue[status_id]' do
3259 3277 assert_select 'option[value="2"]'
3260 3278 assert_select 'option[value="3"]'
3261 3279 assert_select 'option', 2
3262 3280 end
3263 3281 end
3264 3282
3265 3283 def test_put_update_without_custom_fields_param
3266 3284 @request.session[:user_id] = 2
3267 3285
3268 3286 issue = Issue.find(1)
3269 3287 assert_equal '125', issue.custom_value_for(2).value
3270 3288
3271 3289 assert_difference('Journal.count') do
3272 3290 assert_difference('JournalDetail.count') do
3273 3291 put :update, :id => 1, :issue => {:subject => 'New subject'}
3274 3292 end
3275 3293 end
3276 3294 assert_redirected_to :action => 'show', :id => '1'
3277 3295 issue.reload
3278 3296 assert_equal 'New subject', issue.subject
3279 3297 # Make sure custom fields were not cleared
3280 3298 assert_equal '125', issue.custom_value_for(2).value
3281 3299 end
3282 3300
3283 3301 def test_put_update_with_project_change
3284 3302 @request.session[:user_id] = 2
3285 3303 ActionMailer::Base.deliveries.clear
3286 3304
3287 3305 with_settings :notified_events => %w(issue_updated) do
3288 3306 assert_difference('Journal.count') do
3289 3307 assert_difference('JournalDetail.count', 3) do
3290 3308 put :update, :id => 1, :issue => {:project_id => '2',
3291 3309 :tracker_id => '1', # no change
3292 3310 :priority_id => '6',
3293 3311 :category_id => '3'
3294 3312 }
3295 3313 end
3296 3314 end
3297 3315 end
3298 3316 assert_redirected_to :action => 'show', :id => '1'
3299 3317 issue = Issue.find(1)
3300 3318 assert_equal 2, issue.project_id
3301 3319 assert_equal 1, issue.tracker_id
3302 3320 assert_equal 6, issue.priority_id
3303 3321 assert_equal 3, issue.category_id
3304 3322
3305 3323 mail = ActionMailer::Base.deliveries.last
3306 3324 assert_not_nil mail
3307 3325 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
3308 3326 assert_mail_body_match "Project changed from eCookbook to OnlineStore", mail
3309 3327 end
3310 3328
3311 3329 def test_put_update_trying_to_move_issue_to_project_without_tracker_should_not_error
3312 3330 target = Project.generate!(:tracker_ids => [])
3313 3331 assert target.trackers.empty?
3314 3332 issue = Issue.generate!
3315 3333 @request.session[:user_id] = 1
3316 3334
3317 3335 put :update, :id => issue.id, :issue => {:project_id => target.id}
3318 3336 assert_response 302
3319 3337 end
3320 3338
3321 3339 def test_put_update_with_tracker_change
3322 3340 @request.session[:user_id] = 2
3323 3341 ActionMailer::Base.deliveries.clear
3324 3342
3325 3343 with_settings :notified_events => %w(issue_updated) do
3326 3344 assert_difference('Journal.count') do
3327 3345 assert_difference('JournalDetail.count', 2) do
3328 3346 put :update, :id => 1, :issue => {:project_id => '1',
3329 3347 :tracker_id => '2',
3330 3348 :priority_id => '6'
3331 3349 }
3332 3350 end
3333 3351 end
3334 3352 end
3335 3353 assert_redirected_to :action => 'show', :id => '1'
3336 3354 issue = Issue.find(1)
3337 3355 assert_equal 1, issue.project_id
3338 3356 assert_equal 2, issue.tracker_id
3339 3357 assert_equal 6, issue.priority_id
3340 3358 assert_equal 1, issue.category_id
3341 3359
3342 3360 mail = ActionMailer::Base.deliveries.last
3343 3361 assert_not_nil mail
3344 3362 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
3345 3363 assert_mail_body_match "Tracker changed from Bug to Feature request", mail
3346 3364 end
3347 3365
3348 3366 def test_put_update_with_custom_field_change
3349 3367 @request.session[:user_id] = 2
3350 3368 issue = Issue.find(1)
3351 3369 assert_equal '125', issue.custom_value_for(2).value
3352 3370
3353 3371 with_settings :notified_events => %w(issue_updated) do
3354 3372 assert_difference('Journal.count') do
3355 3373 assert_difference('JournalDetail.count', 3) do
3356 3374 put :update, :id => 1, :issue => {:subject => 'Custom field change',
3357 3375 :priority_id => '6',
3358 3376 :category_id => '1', # no change
3359 3377 :custom_field_values => { '2' => 'New custom value' }
3360 3378 }
3361 3379 end
3362 3380 end
3363 3381 end
3364 3382 assert_redirected_to :action => 'show', :id => '1'
3365 3383 issue.reload
3366 3384 assert_equal 'New custom value', issue.custom_value_for(2).value
3367 3385
3368 3386 mail = ActionMailer::Base.deliveries.last
3369 3387 assert_not_nil mail
3370 3388 assert_mail_body_match "Searchable field changed from 125 to New custom value", mail
3371 3389 end
3372 3390
3373 3391 def test_put_update_with_multi_custom_field_change
3374 3392 field = CustomField.find(1)
3375 3393 field.update_attribute :multiple, true
3376 3394 issue = Issue.find(1)
3377 3395 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
3378 3396 issue.save!
3379 3397
3380 3398 @request.session[:user_id] = 2
3381 3399 assert_difference('Journal.count') do
3382 3400 assert_difference('JournalDetail.count', 3) do
3383 3401 put :update, :id => 1,
3384 3402 :issue => {
3385 3403 :subject => 'Custom field change',
3386 3404 :custom_field_values => { '1' => ['', 'Oracle', 'PostgreSQL'] }
3387 3405 }
3388 3406 end
3389 3407 end
3390 3408 assert_redirected_to :action => 'show', :id => '1'
3391 3409 assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
3392 3410 end
3393 3411
3394 3412 def test_put_update_with_status_and_assignee_change
3395 3413 issue = Issue.find(1)
3396 3414 assert_equal 1, issue.status_id
3397 3415 @request.session[:user_id] = 2
3398 3416
3399 3417 with_settings :notified_events => %w(issue_updated) do
3400 3418 assert_difference('TimeEntry.count', 0) do
3401 3419 put :update,
3402 3420 :id => 1,
3403 3421 :issue => { :status_id => 2, :assigned_to_id => 3, :notes => 'Assigned to dlopper' },
3404 3422 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
3405 3423 end
3406 3424 end
3407 3425 assert_redirected_to :action => 'show', :id => '1'
3408 3426 issue.reload
3409 3427 assert_equal 2, issue.status_id
3410 3428 j = Journal.order('id DESC').first
3411 3429 assert_equal 'Assigned to dlopper', j.notes
3412 3430 assert_equal 2, j.details.size
3413 3431
3414 3432 mail = ActionMailer::Base.deliveries.last
3415 3433 assert_mail_body_match "Status changed from New to Assigned", mail
3416 3434 # subject should contain the new status
3417 3435 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
3418 3436 end
3419 3437
3420 3438 def test_put_update_with_note_only
3421 3439 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
3422 3440
3423 3441 with_settings :notified_events => %w(issue_updated) do
3424 3442 # anonymous user
3425 3443 put :update,
3426 3444 :id => 1,
3427 3445 :issue => { :notes => notes }
3428 3446 end
3429 3447 assert_redirected_to :action => 'show', :id => '1'
3430 3448 j = Journal.order('id DESC').first
3431 3449 assert_equal notes, j.notes
3432 3450 assert_equal 0, j.details.size
3433 3451 assert_equal User.anonymous, j.user
3434 3452
3435 3453 mail = ActionMailer::Base.deliveries.last
3436 3454 assert_mail_body_match notes, mail
3437 3455 end
3438 3456
3439 3457 def test_put_update_with_private_note_only
3440 3458 notes = 'Private note'
3441 3459 @request.session[:user_id] = 2
3442 3460
3443 3461 assert_difference 'Journal.count' do
3444 3462 put :update, :id => 1, :issue => {:notes => notes, :private_notes => '1'}
3445 3463 assert_redirected_to :action => 'show', :id => '1'
3446 3464 end
3447 3465
3448 3466 j = Journal.order('id DESC').first
3449 3467 assert_equal notes, j.notes
3450 3468 assert_equal true, j.private_notes
3451 3469 end
3452 3470
3453 3471 def test_put_update_with_private_note_and_changes
3454 3472 notes = 'Private note'
3455 3473 @request.session[:user_id] = 2
3456 3474
3457 3475 assert_difference 'Journal.count', 2 do
3458 3476 put :update, :id => 1, :issue => {:subject => 'New subject', :notes => notes, :private_notes => '1'}
3459 3477 assert_redirected_to :action => 'show', :id => '1'
3460 3478 end
3461 3479
3462 3480 j = Journal.order('id DESC').first
3463 3481 assert_equal notes, j.notes
3464 3482 assert_equal true, j.private_notes
3465 3483 assert_equal 0, j.details.count
3466 3484
3467 3485 j = Journal.order('id DESC').offset(1).first
3468 3486 assert_nil j.notes
3469 3487 assert_equal false, j.private_notes
3470 3488 assert_equal 1, j.details.count
3471 3489 end
3472 3490
3473 3491 def test_put_update_with_note_and_spent_time
3474 3492 @request.session[:user_id] = 2
3475 3493 spent_hours_before = Issue.find(1).spent_hours
3476 3494 assert_difference('TimeEntry.count') do
3477 3495 put :update,
3478 3496 :id => 1,
3479 3497 :issue => { :notes => '2.5 hours added' },
3480 3498 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
3481 3499 end
3482 3500 assert_redirected_to :action => 'show', :id => '1'
3483 3501
3484 3502 issue = Issue.find(1)
3485 3503
3486 3504 j = Journal.order('id DESC').first
3487 3505 assert_equal '2.5 hours added', j.notes
3488 3506 assert_equal 0, j.details.size
3489 3507
3490 3508 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
3491 3509 assert_not_nil t
3492 3510 assert_equal 2.5, t.hours
3493 3511 assert_equal spent_hours_before + 2.5, issue.spent_hours
3494 3512 end
3495 3513
3496 3514 def test_put_update_should_preserve_parent_issue_even_if_not_visible
3497 3515 parent = Issue.generate!(:project_id => 1, :is_private => true)
3498 3516 issue = Issue.generate!(:parent_issue_id => parent.id)
3499 3517 assert !parent.visible?(User.find(3))
3500 3518 @request.session[:user_id] = 3
3501 3519
3502 3520 get :edit, :id => issue.id
3503 3521 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', parent.id.to_s
3504 3522
3505 3523 put :update, :id => issue.id, :issue => {:subject => 'New subject', :parent_issue_id => parent.id.to_s}
3506 3524 assert_response 302
3507 3525 assert_equal parent, issue.parent
3508 3526 end
3509 3527
3510 3528 def test_put_update_with_attachment_only
3511 3529 set_tmp_attachments_directory
3512 3530
3513 3531 # Delete all fixtured journals, a race condition can occur causing the wrong
3514 3532 # journal to get fetched in the next find.
3515 3533 Journal.delete_all
3516 3534
3517 3535 with_settings :notified_events => %w(issue_updated) do
3518 3536 # anonymous user
3519 3537 assert_difference 'Attachment.count' do
3520 3538 put :update, :id => 1,
3521 3539 :issue => {:notes => ''},
3522 3540 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
3523 3541 end
3524 3542 end
3525 3543
3526 3544 assert_redirected_to :action => 'show', :id => '1'
3527 3545 j = Issue.find(1).journals.reorder('id DESC').first
3528 3546 assert j.notes.blank?
3529 3547 assert_equal 1, j.details.size
3530 3548 assert_equal 'testfile.txt', j.details.first.value
3531 3549 assert_equal User.anonymous, j.user
3532 3550
3533 3551 attachment = Attachment.order('id DESC').first
3534 3552 assert_equal Issue.find(1), attachment.container
3535 3553 assert_equal User.anonymous, attachment.author
3536 3554 assert_equal 'testfile.txt', attachment.filename
3537 3555 assert_equal 'text/plain', attachment.content_type
3538 3556 assert_equal 'test file', attachment.description
3539 3557 assert_equal 59, attachment.filesize
3540 3558 assert File.exists?(attachment.diskfile)
3541 3559 assert_equal 59, File.size(attachment.diskfile)
3542 3560
3543 3561 mail = ActionMailer::Base.deliveries.last
3544 3562 assert_mail_body_match 'testfile.txt', mail
3545 3563 end
3546 3564
3547 3565 def test_put_update_with_failure_should_save_attachments
3548 3566 set_tmp_attachments_directory
3549 3567 @request.session[:user_id] = 2
3550 3568
3551 3569 assert_no_difference 'Journal.count' do
3552 3570 assert_difference 'Attachment.count' do
3553 3571 put :update, :id => 1,
3554 3572 :issue => { :subject => '' },
3555 3573 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
3556 3574 assert_response :success
3557 3575 end
3558 3576 end
3559 3577
3560 3578 attachment = Attachment.order('id DESC').first
3561 3579 assert_equal 'testfile.txt', attachment.filename
3562 3580 assert File.exists?(attachment.diskfile)
3563 3581 assert_nil attachment.container
3564 3582
3565 3583 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
3566 3584 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
3567 3585 end
3568 3586
3569 3587 def test_put_update_with_failure_should_keep_saved_attachments
3570 3588 set_tmp_attachments_directory
3571 3589 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
3572 3590 @request.session[:user_id] = 2
3573 3591
3574 3592 assert_no_difference 'Journal.count' do
3575 3593 assert_no_difference 'Attachment.count' do
3576 3594 put :update, :id => 1,
3577 3595 :issue => { :subject => '' },
3578 3596 :attachments => {'p0' => {'token' => attachment.token}}
3579 3597 assert_response :success
3580 3598 end
3581 3599 end
3582 3600
3583 3601 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
3584 3602 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
3585 3603 end
3586 3604
3587 3605 def test_put_update_should_attach_saved_attachments
3588 3606 set_tmp_attachments_directory
3589 3607 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
3590 3608 @request.session[:user_id] = 2
3591 3609
3592 3610 assert_difference 'Journal.count' do
3593 3611 assert_difference 'JournalDetail.count' do
3594 3612 assert_no_difference 'Attachment.count' do
3595 3613 put :update, :id => 1,
3596 3614 :issue => {:notes => 'Attachment added'},
3597 3615 :attachments => {'p0' => {'token' => attachment.token}}
3598 3616 assert_redirected_to '/issues/1'
3599 3617 end
3600 3618 end
3601 3619 end
3602 3620
3603 3621 attachment.reload
3604 3622 assert_equal Issue.find(1), attachment.container
3605 3623
3606 3624 journal = Journal.order('id DESC').first
3607 3625 assert_equal 1, journal.details.size
3608 3626 assert_equal 'testfile.txt', journal.details.first.value
3609 3627 end
3610 3628
3611 3629 def test_put_update_with_attachment_that_fails_to_save
3612 3630 set_tmp_attachments_directory
3613 3631
3614 3632 # anonymous user
3615 3633 with_settings :attachment_max_size => 0 do
3616 3634 put :update,
3617 3635 :id => 1,
3618 3636 :issue => {:notes => ''},
3619 3637 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
3620 3638 assert_redirected_to :action => 'show', :id => '1'
3621 3639 assert_equal '1 file(s) could not be saved.', flash[:warning]
3622 3640 end
3623 3641 end
3624 3642
3625 3643 def test_put_update_with_attachment_deletion_should_create_a_single_journal
3626 3644 set_tmp_attachments_directory
3627 3645 @request.session[:user_id] = 2
3628 3646
3629 3647 journal = new_record(Journal) do
3630 3648 assert_difference 'Attachment.count', -2 do
3631 3649 put :update,
3632 3650 :id => 3,
3633 3651 :issue => {
3634 3652 :notes => 'Removing attachments',
3635 3653 :deleted_attachment_ids => ['1', '5']
3636 3654 }
3637 3655 end
3638 3656 end
3639 3657 assert_equal 'Removing attachments', journal.notes
3640 3658 assert_equal 2, journal.details.count
3641 3659 end
3642 3660
3643 3661 def test_put_update_with_attachment_deletion_and_failure_should_preserve_selected_attachments
3644 3662 set_tmp_attachments_directory
3645 3663 @request.session[:user_id] = 2
3646 3664
3647 3665 assert_no_difference 'Journal.count' do
3648 3666 assert_no_difference 'Attachment.count' do
3649 3667 put :update,
3650 3668 :id => 3,
3651 3669 :issue => {
3652 3670 :subject => '',
3653 3671 :notes => 'Removing attachments',
3654 3672 :deleted_attachment_ids => ['1', '5']
3655 3673 }
3656 3674 end
3657 3675 end
3658 3676 assert_select 'input[name=?][value="1"][checked=checked]', 'issue[deleted_attachment_ids][]'
3659 3677 assert_select 'input[name=?][value="5"][checked=checked]', 'issue[deleted_attachment_ids][]'
3660 3678 assert_select 'input[name=?][value="6"]:not([checked])', 'issue[deleted_attachment_ids][]'
3661 3679 end
3662 3680
3663 3681 def test_put_update_with_no_change
3664 3682 issue = Issue.find(1)
3665 3683 issue.journals.clear
3666 3684 ActionMailer::Base.deliveries.clear
3667 3685
3668 3686 put :update,
3669 3687 :id => 1,
3670 3688 :issue => {:notes => ''}
3671 3689 assert_redirected_to :action => 'show', :id => '1'
3672 3690
3673 3691 issue.reload
3674 3692 assert issue.journals.empty?
3675 3693 # No email should be sent
3676 3694 assert ActionMailer::Base.deliveries.empty?
3677 3695 end
3678 3696
3679 3697 def test_put_update_should_send_a_notification
3680 3698 @request.session[:user_id] = 2
3681 3699 ActionMailer::Base.deliveries.clear
3682 3700 issue = Issue.find(1)
3683 3701 old_subject = issue.subject
3684 3702 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
3685 3703
3686 3704 with_settings :notified_events => %w(issue_updated) do
3687 3705 put :update, :id => 1, :issue => {:subject => new_subject,
3688 3706 :priority_id => '6',
3689 3707 :category_id => '1' # no change
3690 3708 }
3691 3709 assert_equal 1, ActionMailer::Base.deliveries.size
3692 3710 end
3693 3711 end
3694 3712
3695 3713 def test_put_update_with_invalid_spent_time_hours_only
3696 3714 @request.session[:user_id] = 2
3697 3715 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3698 3716
3699 3717 assert_no_difference('Journal.count') do
3700 3718 put :update,
3701 3719 :id => 1,
3702 3720 :issue => {:notes => notes},
3703 3721 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
3704 3722 end
3705 3723 assert_response :success
3706 3724
3707 3725 assert_select_error /Activity cannot be blank/
3708 3726 assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
3709 3727 assert_select 'input[name=?][value=?]', 'time_entry[hours]', '2z'
3710 3728 end
3711 3729
3712 3730 def test_put_update_with_invalid_spent_time_comments_only
3713 3731 @request.session[:user_id] = 2
3714 3732 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3715 3733
3716 3734 assert_no_difference('Journal.count') do
3717 3735 put :update,
3718 3736 :id => 1,
3719 3737 :issue => {:notes => notes},
3720 3738 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
3721 3739 end
3722 3740 assert_response :success
3723 3741
3724 3742 assert_select_error /Activity cannot be blank/
3725 3743 assert_select_error /Hours cannot be blank/
3726 3744 assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
3727 3745 assert_select 'input[name=?][value=?]', 'time_entry[comments]', 'this is my comment'
3728 3746 end
3729 3747
3730 3748 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
3731 3749 issue = Issue.find(2)
3732 3750 @request.session[:user_id] = 2
3733 3751
3734 3752 put :update,
3735 3753 :id => issue.id,
3736 3754 :issue => {
3737 3755 :fixed_version_id => 4
3738 3756 }
3739 3757
3740 3758 assert_response :redirect
3741 3759 issue.reload
3742 3760 assert_equal 4, issue.fixed_version_id
3743 3761 assert_not_equal issue.project_id, issue.fixed_version.project_id
3744 3762 end
3745 3763
3746 3764 def test_put_update_should_redirect_back_using_the_back_url_parameter
3747 3765 issue = Issue.find(2)
3748 3766 @request.session[:user_id] = 2
3749 3767
3750 3768 put :update,
3751 3769 :id => issue.id,
3752 3770 :issue => {
3753 3771 :fixed_version_id => 4
3754 3772 },
3755 3773 :back_url => '/issues'
3756 3774
3757 3775 assert_response :redirect
3758 3776 assert_redirected_to '/issues'
3759 3777 end
3760 3778
3761 3779 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
3762 3780 issue = Issue.find(2)
3763 3781 @request.session[:user_id] = 2
3764 3782
3765 3783 put :update,
3766 3784 :id => issue.id,
3767 3785 :issue => {
3768 3786 :fixed_version_id => 4
3769 3787 },
3770 3788 :back_url => 'http://google.com'
3771 3789
3772 3790 assert_response :redirect
3773 3791 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
3774 3792 end
3775 3793
3776 3794 def test_put_update_should_redirect_with_previous_and_next_issue_ids_params
3777 3795 @request.session[:user_id] = 2
3778 3796
3779 3797 put :update, :id => 11,
3780 3798 :issue => {:status_id => 6, :notes => 'Notes'},
3781 3799 :prev_issue_id => 8,
3782 3800 :next_issue_id => 12,
3783 3801 :issue_position => 2,
3784 3802 :issue_count => 3
3785 3803
3786 3804 assert_redirected_to '/issues/11?issue_count=3&issue_position=2&next_issue_id=12&prev_issue_id=8'
3787 3805 end
3788 3806
3789 3807 def test_update_with_permission_on_tracker_should_be_allowed
3790 3808 role = Role.find(1)
3791 3809 role.set_permission_trackers :edit_issues, [1]
3792 3810 role.save!
3793 3811 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Original subject')
3794 3812
3795 3813 @request.session[:user_id] = 2
3796 3814 put :update, :id => issue.id, :issue => {:subject => 'Changed subject'}
3797 3815 assert_response 302
3798 3816 assert_equal 'Changed subject', issue.reload.subject
3799 3817 end
3800 3818
3801 3819 def test_update_without_permission_on_tracker_should_be_denied
3802 3820 role = Role.find(1)
3803 3821 role.set_permission_trackers :edit_issues, [1]
3804 3822 role.save!
3805 3823 issue = Issue.generate!(:project_id => 1, :tracker_id => 2, :subject => 'Original subject')
3806 3824
3807 3825 @request.session[:user_id] = 2
3808 3826 put :update, :id => issue.id, :issue => {:subject => 'Changed subject'}
3809 3827 assert_response 302
3810 3828 assert_equal 'Original subject', issue.reload.subject
3811 3829 end
3812 3830
3813 3831 def test_get_bulk_edit
3814 3832 @request.session[:user_id] = 2
3815 3833 get :bulk_edit, :ids => [1, 3]
3816 3834 assert_response :success
3817 3835
3818 3836 assert_select 'ul#bulk-selection' do
3819 3837 assert_select 'li', 2
3820 3838 assert_select 'li a', :text => 'Bug #1'
3821 3839 end
3822 3840
3823 3841 assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
3824 3842 assert_select 'input[name=?]', 'ids[]', 2
3825 3843 assert_select 'input[name=?][value="1"][type=hidden]', 'ids[]'
3826 3844
3827 3845 assert_select 'select[name=?]', 'issue[project_id]'
3828 3846 assert_select 'input[name=?]', 'issue[parent_issue_id]'
3829 3847
3830 3848 # Project specific custom field, date type
3831 3849 field = CustomField.find(9)
3832 3850 assert !field.is_for_all?
3833 3851 assert_equal 'date', field.field_format
3834 3852 assert_select 'input[name=?]', 'issue[custom_field_values][9]'
3835 3853
3836 3854 # System wide custom field
3837 3855 assert CustomField.find(1).is_for_all?
3838 3856 assert_select 'select[name=?]', 'issue[custom_field_values][1]'
3839 3857
3840 3858 # Be sure we don't display inactive IssuePriorities
3841 3859 assert ! IssuePriority.find(15).active?
3842 3860 assert_select 'select[name=?]', 'issue[priority_id]' do
3843 3861 assert_select 'option[value="15"]', 0
3844 3862 end
3845 3863 end
3846 3864 end
3847 3865
3848 3866 def test_get_bulk_edit_on_different_projects
3849 3867 @request.session[:user_id] = 2
3850 3868 get :bulk_edit, :ids => [1, 2, 6]
3851 3869 assert_response :success
3852 3870
3853 3871 # Can not set issues from different projects as children of an issue
3854 3872 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
3855 3873
3856 3874 # Project specific custom field, date type
3857 3875 field = CustomField.find(9)
3858 3876 assert !field.is_for_all?
3859 3877 assert !field.project_ids.include?(Issue.find(6).project_id)
3860 3878 assert_select 'input[name=?]', 'issue[custom_field_values][9]', 0
3861 3879 end
3862 3880
3863 3881 def test_get_bulk_edit_with_user_custom_field
3864 3882 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true, :tracker_ids => [1,2,3])
3865 3883
3866 3884 @request.session[:user_id] = 2
3867 3885 get :bulk_edit, :ids => [1, 2]
3868 3886 assert_response :success
3869 3887
3870 3888 assert_select 'select.user_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
3871 3889 assert_select 'option', Project.find(1).users.count + 2 # "no change" + "none" options
3872 3890 end
3873 3891 end
3874 3892
3875 3893 def test_get_bulk_edit_with_version_custom_field
3876 3894 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
3877 3895
3878 3896 @request.session[:user_id] = 2
3879 3897 get :bulk_edit, :ids => [1, 2]
3880 3898 assert_response :success
3881 3899
3882 3900 assert_select 'select.version_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
3883 3901 assert_select 'option', Project.find(1).shared_versions.count + 2 # "no change" + "none" options
3884 3902 end
3885 3903 end
3886 3904
3887 3905 def test_get_bulk_edit_with_multi_custom_field
3888 3906 field = CustomField.find(1)
3889 3907 field.update_attribute :multiple, true
3890 3908
3891 3909 @request.session[:user_id] = 2
3892 3910 get :bulk_edit, :ids => [1, 3]
3893 3911 assert_response :success
3894 3912
3895 3913 assert_select 'select[name=?]', 'issue[custom_field_values][1][]' do
3896 3914 assert_select 'option', field.possible_values.size + 1 # "none" options
3897 3915 end
3898 3916 end
3899 3917
3900 3918 def test_bulk_edit_should_propose_to_clear_text_custom_fields
3901 3919 @request.session[:user_id] = 2
3902 3920 get :bulk_edit, :ids => [1, 3]
3903 3921 assert_response :success
3904 3922
3905 3923 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', '__none__'
3906 3924 end
3907 3925
3908 3926 def test_bulk_edit_should_only_propose_statuses_allowed_for_all_issues
3909 3927 WorkflowTransition.delete_all
3910 3928 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3911 3929 :old_status_id => 1, :new_status_id => 1)
3912 3930 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3913 3931 :old_status_id => 1, :new_status_id => 3)
3914 3932 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3915 3933 :old_status_id => 1, :new_status_id => 4)
3916 3934 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3917 3935 :old_status_id => 2, :new_status_id => 1)
3918 3936 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3919 3937 :old_status_id => 2, :new_status_id => 3)
3920 3938 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3921 3939 :old_status_id => 2, :new_status_id => 5)
3922 3940 @request.session[:user_id] = 2
3923 3941 get :bulk_edit, :ids => [1, 2]
3924 3942
3925 3943 assert_select 'select[name=?]', 'issue[status_id]' do
3926 3944 assert_select 'option[value=""]'
3927 3945 assert_select 'option[value="1"]'
3928 3946 assert_select 'option[value="3"]'
3929 3947 assert_select 'option', 3 # 2 statuses + "no change" option
3930 3948 end
3931 3949 end
3932 3950
3933 3951 def test_bulk_edit_should_propose_target_project_open_shared_versions
3934 3952 @request.session[:user_id] = 2
3935 3953 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3936 3954 assert_response :success
3937 3955
3938 3956 expected_versions = Project.find(1).shared_versions.open.to_a.sort
3939 3957
3940 3958 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
3941 3959 expected_versions.each do |version|
3942 3960 assert_select 'option[value=?]', version.id.to_s
3943 3961 end
3944 3962 assert_select 'option[value=""]'
3945 3963 assert_select 'option[value="none"]'
3946 3964 assert_select 'option', expected_versions.size + 2
3947 3965 end
3948 3966 end
3949 3967
3950 3968 def test_bulk_edit_should_propose_target_project_categories
3951 3969 @request.session[:user_id] = 2
3952 3970 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3953 3971 assert_response :success
3954 3972
3955 3973 expected_categories = Project.find(1).issue_categories.sort
3956 3974
3957 3975 assert_select 'select[name=?]', 'issue[category_id]' do
3958 3976 expected_categories.each do |category|
3959 3977 assert_select 'option[value=?]', category.id.to_s
3960 3978 end
3961 3979 assert_select 'option[value=""]'
3962 3980 assert_select 'option[value="none"]'
3963 3981 assert_select 'option', expected_categories.size + 2
3964 3982 end
3965 3983 end
3966 3984
3967 3985 def test_bulk_edit_should_only_propose_issues_trackers_custom_fields
3968 3986 IssueCustomField.delete_all
3969 3987 field1 = IssueCustomField.generate!(:tracker_ids => [1], :is_for_all => true)
3970 3988 field2 = IssueCustomField.generate!(:tracker_ids => [2], :is_for_all => true)
3971 3989 @request.session[:user_id] = 2
3972 3990
3973 3991 issue_ids = Issue.where(:project_id => 1, :tracker_id => 1).limit(2).ids
3974 3992 get :bulk_edit, :ids => issue_ids
3975 3993 assert_response :success
3976 3994
3977 3995 assert_select 'input[name=?]', "issue[custom_field_values][#{field1.id}]"
3978 3996 assert_select 'input[name=?]', "issue[custom_field_values][#{field2.id}]", 0
3979 3997 end
3980 3998
3981 3999 def test_bulk_update
3982 4000 @request.session[:user_id] = 2
3983 4001 # update issues priority
3984 4002 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
3985 4003 :issue => {:priority_id => 7,
3986 4004 :assigned_to_id => '',
3987 4005 :custom_field_values => {'2' => ''}}
3988 4006
3989 4007 assert_response 302
3990 4008 # check that the issues were updated
3991 4009 assert_equal [7, 7], Issue.where(:id =>[1, 2]).collect {|i| i.priority.id}
3992 4010
3993 4011 issue = Issue.find(1)
3994 4012 journal = issue.journals.reorder('created_on DESC').first
3995 4013 assert_equal '125', issue.custom_value_for(2).value
3996 4014 assert_equal 'Bulk editing', journal.notes
3997 4015 assert_equal 1, journal.details.size
3998 4016 end
3999 4017
4000 4018 def test_bulk_update_with_group_assignee
4001 4019 group = Group.find(11)
4002 4020 project = Project.find(1)
4003 4021 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
4004 4022
4005 4023 @request.session[:user_id] = 2
4006 4024 # update issues assignee
4007 4025 with_settings :issue_group_assignment => '1' do
4008 4026 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
4009 4027 :issue => {:priority_id => '',
4010 4028 :assigned_to_id => group.id,
4011 4029 :custom_field_values => {'2' => ''}}
4012 4030
4013 4031 assert_response 302
4014 4032 assert_equal [group, group], Issue.where(:id => [1, 2]).collect {|i| i.assigned_to}
4015 4033 end
4016 4034 end
4017 4035
4018 4036 def test_bulk_update_on_different_projects
4019 4037 @request.session[:user_id] = 2
4020 4038 # update issues priority
4021 4039 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
4022 4040 :issue => {:priority_id => 7,
4023 4041 :assigned_to_id => '',
4024 4042 :custom_field_values => {'2' => ''}}
4025 4043
4026 4044 assert_response 302
4027 4045 # check that the issues were updated
4028 4046 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
4029 4047
4030 4048 issue = Issue.find(1)
4031 4049 journal = issue.journals.reorder('created_on DESC').first
4032 4050 assert_equal '125', issue.custom_value_for(2).value
4033 4051 assert_equal 'Bulk editing', journal.notes
4034 4052 assert_equal 1, journal.details.size
4035 4053 end
4036 4054
4037 4055 def test_bulk_update_on_different_projects_without_rights
4038 4056 @request.session[:user_id] = 3
4039 4057 user = User.find(3)
4040 4058 action = { :controller => "issues", :action => "bulk_update" }
4041 4059 assert user.allowed_to?(action, Issue.find(1).project)
4042 4060 assert ! user.allowed_to?(action, Issue.find(6).project)
4043 4061 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
4044 4062 :issue => {:priority_id => 7,
4045 4063 :assigned_to_id => '',
4046 4064 :custom_field_values => {'2' => ''}}
4047 4065 assert_response 403
4048 4066 assert_not_equal "Bulk should fail", Journal.last.notes
4049 4067 end
4050 4068
4051 4069 def test_bullk_update_should_send_a_notification
4052 4070 @request.session[:user_id] = 2
4053 4071 ActionMailer::Base.deliveries.clear
4054 4072 with_settings :notified_events => %w(issue_updated) do
4055 4073 post(:bulk_update,
4056 4074 {
4057 4075 :ids => [1, 2],
4058 4076 :notes => 'Bulk editing',
4059 4077 :issue => {
4060 4078 :priority_id => 7,
4061 4079 :assigned_to_id => '',
4062 4080 :custom_field_values => {'2' => ''}
4063 4081 }
4064 4082 })
4065 4083 assert_response 302
4066 4084 assert_equal 2, ActionMailer::Base.deliveries.size
4067 4085 end
4068 4086 end
4069 4087
4070 4088 def test_bulk_update_project
4071 4089 @request.session[:user_id] = 2
4072 4090 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
4073 4091 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4074 4092 # Issues moved to project 2
4075 4093 assert_equal 2, Issue.find(1).project_id
4076 4094 assert_equal 2, Issue.find(2).project_id
4077 4095 # No tracker change
4078 4096 assert_equal 1, Issue.find(1).tracker_id
4079 4097 assert_equal 2, Issue.find(2).tracker_id
4080 4098 end
4081 4099
4082 4100 def test_bulk_update_project_on_single_issue_should_follow_when_needed
4083 4101 @request.session[:user_id] = 2
4084 4102 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
4085 4103 assert_redirected_to '/issues/1'
4086 4104 end
4087 4105
4088 4106 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
4089 4107 @request.session[:user_id] = 2
4090 4108 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
4091 4109 assert_redirected_to '/projects/onlinestore/issues'
4092 4110 end
4093 4111
4094 4112 def test_bulk_update_tracker
4095 4113 @request.session[:user_id] = 2
4096 4114 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
4097 4115 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4098 4116 assert_equal 2, Issue.find(1).tracker_id
4099 4117 assert_equal 2, Issue.find(2).tracker_id
4100 4118 end
4101 4119
4102 4120 def test_bulk_update_status
4103 4121 @request.session[:user_id] = 2
4104 4122 # update issues priority
4105 4123 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
4106 4124 :issue => {:priority_id => '',
4107 4125 :assigned_to_id => '',
4108 4126 :status_id => '5'}
4109 4127
4110 4128 assert_response 302
4111 4129 issue = Issue.find(1)
4112 4130 assert issue.closed?
4113 4131 end
4114 4132
4115 4133 def test_bulk_update_priority
4116 4134 @request.session[:user_id] = 2
4117 4135 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
4118 4136
4119 4137 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4120 4138 assert_equal 6, Issue.find(1).priority_id
4121 4139 assert_equal 6, Issue.find(2).priority_id
4122 4140 end
4123 4141
4124 4142 def test_bulk_update_with_notes
4125 4143 @request.session[:user_id] = 2
4126 4144 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
4127 4145
4128 4146 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4129 4147 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
4130 4148 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
4131 4149 end
4132 4150
4133 4151 def test_bulk_update_parent_id
4134 4152 IssueRelation.delete_all
4135 4153 @request.session[:user_id] = 2
4136 4154 post :bulk_update, :ids => [1, 3],
4137 4155 :notes => 'Bulk editing parent',
4138 4156 :issue => {:priority_id => '', :assigned_to_id => '',
4139 4157 :status_id => '', :parent_issue_id => '2'}
4140 4158 assert_response 302
4141 4159 parent = Issue.find(2)
4142 4160 assert_equal parent.id, Issue.find(1).parent_id
4143 4161 assert_equal parent.id, Issue.find(3).parent_id
4144 4162 assert_equal [1, 3], parent.children.collect(&:id).sort
4145 4163 end
4146 4164
4147 4165 def test_bulk_update_estimated_hours
4148 4166 @request.session[:user_id] = 2
4149 4167 post :bulk_update, :ids => [1, 2], :issue => {:estimated_hours => 4.25}
4150 4168
4151 4169 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4152 4170 assert_equal 4.25, Issue.find(1).estimated_hours
4153 4171 assert_equal 4.25, Issue.find(2).estimated_hours
4154 4172 end
4155 4173
4156 4174 def test_bulk_update_custom_field
4157 4175 @request.session[:user_id] = 2
4158 4176 # update issues priority
4159 4177 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
4160 4178 :issue => {:priority_id => '',
4161 4179 :assigned_to_id => '',
4162 4180 :custom_field_values => {'2' => '777'}}
4163 4181
4164 4182 assert_response 302
4165 4183
4166 4184 issue = Issue.find(1)
4167 4185 journal = issue.journals.reorder('created_on DESC').first
4168 4186 assert_equal '777', issue.custom_value_for(2).value
4169 4187 assert_equal 1, journal.details.size
4170 4188 assert_equal '125', journal.details.first.old_value
4171 4189 assert_equal '777', journal.details.first.value
4172 4190 end
4173 4191
4174 4192 def test_bulk_update_custom_field_to_blank
4175 4193 @request.session[:user_id] = 2
4176 4194 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing custom field',
4177 4195 :issue => {:priority_id => '',
4178 4196 :assigned_to_id => '',
4179 4197 :custom_field_values => {'1' => '__none__'}}
4180 4198 assert_response 302
4181 4199 assert_equal '', Issue.find(1).custom_field_value(1)
4182 4200 assert_equal '', Issue.find(3).custom_field_value(1)
4183 4201 end
4184 4202
4185 4203 def test_bulk_update_multi_custom_field
4186 4204 field = CustomField.find(1)
4187 4205 field.update_attribute :multiple, true
4188 4206
4189 4207 @request.session[:user_id] = 2
4190 4208 post :bulk_update, :ids => [1, 2, 3], :notes => 'Bulk editing multi custom field',
4191 4209 :issue => {:priority_id => '',
4192 4210 :assigned_to_id => '',
4193 4211 :custom_field_values => {'1' => ['MySQL', 'Oracle']}}
4194 4212
4195 4213 assert_response 302
4196 4214
4197 4215 assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
4198 4216 assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
4199 4217 # the custom field is not associated with the issue tracker
4200 4218 assert_nil Issue.find(2).custom_field_value(1)
4201 4219 end
4202 4220
4203 4221 def test_bulk_update_multi_custom_field_to_blank
4204 4222 field = CustomField.find(1)
4205 4223 field.update_attribute :multiple, true
4206 4224
4207 4225 @request.session[:user_id] = 2
4208 4226 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing multi custom field',
4209 4227 :issue => {:priority_id => '',
4210 4228 :assigned_to_id => '',
4211 4229 :custom_field_values => {'1' => ['__none__']}}
4212 4230 assert_response 302
4213 4231 assert_equal [''], Issue.find(1).custom_field_value(1)
4214 4232 assert_equal [''], Issue.find(3).custom_field_value(1)
4215 4233 end
4216 4234
4217 4235 def test_bulk_update_unassign
4218 4236 assert_not_nil Issue.find(2).assigned_to
4219 4237 @request.session[:user_id] = 2
4220 4238 # unassign issues
4221 4239 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
4222 4240 assert_response 302
4223 4241 # check that the issues were updated
4224 4242 assert_nil Issue.find(2).assigned_to
4225 4243 end
4226 4244
4227 4245 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
4228 4246 @request.session[:user_id] = 2
4229 4247
4230 4248 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
4231 4249
4232 4250 assert_response :redirect
4233 4251 issues = Issue.find([1,2])
4234 4252 issues.each do |issue|
4235 4253 assert_equal 4, issue.fixed_version_id
4236 4254 assert_not_equal issue.project_id, issue.fixed_version.project_id
4237 4255 end
4238 4256 end
4239 4257
4240 4258 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
4241 4259 @request.session[:user_id] = 2
4242 4260 post :bulk_update, :ids => [1,2], :back_url => '/issues'
4243 4261
4244 4262 assert_response :redirect
4245 4263 assert_redirected_to '/issues'
4246 4264 end
4247 4265
4248 4266 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
4249 4267 @request.session[:user_id] = 2
4250 4268 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
4251 4269
4252 4270 assert_response :redirect
4253 4271 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
4254 4272 end
4255 4273
4256 4274 def test_bulk_update_with_all_failures_should_show_errors
4257 4275 @request.session[:user_id] = 2
4258 4276 post :bulk_update, :ids => [1, 2], :issue => {:start_date => 'foo'}
4259 4277 assert_response :success
4260 4278
4261 4279 assert_select '#errorExplanation span', :text => 'Failed to save 2 issue(s) on 2 selected: #1, #2.'
4262 4280 assert_select '#errorExplanation ul li', :text => 'Start date is not a valid date: #1, #2'
4263 4281 end
4264 4282
4265 4283 def test_bulk_update_with_some_failures_should_show_errors
4266 4284 issue1 = Issue.generate!(:start_date => '2013-05-12')
4267 4285 issue2 = Issue.generate!(:start_date => '2013-05-15')
4268 4286 issue3 = Issue.generate!
4269 4287 @request.session[:user_id] = 2
4270 4288 post :bulk_update, :ids => [issue1.id, issue2.id, issue3.id],
4271 4289 :issue => {:due_date => '2013-05-01'}
4272 4290 assert_response :success
4273 4291
4274 4292 assert_select '#errorExplanation span',
4275 4293 :text => "Failed to save 2 issue(s) on 3 selected: ##{issue1.id}, ##{issue2.id}."
4276 4294 assert_select '#errorExplanation ul li',
4277 4295 :text => "Due date must be greater than start date: ##{issue1.id}, ##{issue2.id}"
4278 4296
4279 4297 assert_select '#bulk-selection li', 2
4280 4298 end
4281 4299
4282 4300 def test_bulk_update_with_failure_should_preserved_form_values
4283 4301 @request.session[:user_id] = 2
4284 4302 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2', :start_date => 'foo'}
4285 4303 assert_response :success
4286 4304
4287 4305 assert_select 'select[name=?]', 'issue[tracker_id]' do
4288 4306 assert_select 'option[value="2"][selected=selected]'
4289 4307 end
4290 4308 assert_select 'input[name=?][value=?]', 'issue[start_date]', 'foo'
4291 4309 end
4292 4310
4293 4311 def test_get_bulk_copy
4294 4312 @request.session[:user_id] = 2
4295 4313 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
4296 4314 assert_response :success
4297 4315
4298 4316 assert_select '#bulk-selection li', 3
4299 4317
4300 4318 assert_select 'select[name=?]', 'issue[project_id]' do
4301 4319 assert_select 'option[value=""]'
4302 4320 end
4303 4321 assert_select 'input[name=copy_attachments]'
4304 4322 end
4305 4323
4306 4324 def test_get_bulk_copy_without_add_issues_permission_should_not_propose_current_project_as_target
4307 4325 user = setup_user_with_copy_but_not_add_permission
4308 4326 @request.session[:user_id] = user.id
4309 4327
4310 4328 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
4311 4329 assert_response :success
4312 4330
4313 4331 assert_select 'select[name=?]', 'issue[project_id]' do
4314 4332 assert_select 'option[value=""]', 0
4315 4333 assert_select 'option[value="2"]'
4316 4334 end
4317 4335 end
4318 4336
4319 4337 def test_bulk_copy_to_another_project
4320 4338 @request.session[:user_id] = 2
4321 4339 assert_difference 'Issue.count', 2 do
4322 4340 assert_no_difference 'Project.find(1).issues.count' do
4323 4341 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
4324 4342 end
4325 4343 end
4326 4344 assert_redirected_to '/projects/ecookbook/issues'
4327 4345
4328 4346 copies = Issue.order('id DESC').limit(issues.size)
4329 4347 copies.each do |copy|
4330 4348 assert_equal 2, copy.project_id
4331 4349 end
4332 4350 end
4333 4351
4334 4352 def test_bulk_copy_without_add_issues_permission_should_be_allowed_on_project_with_permission
4335 4353 user = setup_user_with_copy_but_not_add_permission
4336 4354 @request.session[:user_id] = user.id
4337 4355
4338 4356 assert_difference 'Issue.count', 3 do
4339 4357 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '2'}, :copy => '1'
4340 4358 assert_response 302
4341 4359 end
4342 4360 end
4343 4361
4344 4362 def test_bulk_copy_on_same_project_without_add_issues_permission_should_be_denied
4345 4363 user = setup_user_with_copy_but_not_add_permission
4346 4364 @request.session[:user_id] = user.id
4347 4365
4348 4366 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => ''}, :copy => '1'
4349 4367 assert_response 403
4350 4368 end
4351 4369
4352 4370 def test_bulk_copy_on_different_project_without_add_issues_permission_should_be_denied
4353 4371 user = setup_user_with_copy_but_not_add_permission
4354 4372 @request.session[:user_id] = user.id
4355 4373
4356 4374 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '1'}, :copy => '1'
4357 4375 assert_response 403
4358 4376 end
4359 4377
4360 4378 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
4361 4379 @request.session[:user_id] = 2
4362 4380 issues = [
4363 4381 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
4364 4382 :priority_id => 2, :subject => 'issue 1', :author_id => 1,
4365 4383 :assigned_to_id => nil),
4366 4384 Issue.create!(:project_id => 2, :tracker_id => 3, :status_id => 2,
4367 4385 :priority_id => 1, :subject => 'issue 2', :author_id => 2,
4368 4386 :assigned_to_id => 3)
4369 4387 ]
4370 4388 assert_difference 'Issue.count', issues.size do
4371 4389 post :bulk_update, :ids => issues.map(&:id), :copy => '1',
4372 4390 :issue => {
4373 4391 :project_id => '', :tracker_id => '', :assigned_to_id => '',
4374 4392 :status_id => '', :start_date => '', :due_date => ''
4375 4393 }
4376 4394 end
4377 4395
4378 4396 copies = Issue.order('id DESC').limit(issues.size)
4379 4397 issues.each do |orig|
4380 4398 copy = copies.detect {|c| c.subject == orig.subject}
4381 4399 assert_not_nil copy
4382 4400 assert_equal orig.project_id, copy.project_id
4383 4401 assert_equal orig.tracker_id, copy.tracker_id
4384 4402 assert_equal orig.status_id, copy.status_id
4385 4403 assert_equal orig.assigned_to_id, copy.assigned_to_id
4386 4404 assert_equal orig.priority_id, copy.priority_id
4387 4405 end
4388 4406 end
4389 4407
4390 4408 def test_bulk_copy_should_allow_changing_the_issue_attributes
4391 4409 # Fixes random test failure with Mysql
4392 4410 # where Issue.where(:project_id => 2).limit(2).order('id desc')
4393 4411 # doesn't return the expected results
4394 4412 Issue.where("project_id=2").delete_all
4395 4413
4396 4414 @request.session[:user_id] = 2
4397 4415 assert_difference 'Issue.count', 2 do
4398 4416 assert_no_difference 'Project.find(1).issues.count' do
4399 4417 post :bulk_update, :ids => [1, 2], :copy => '1',
4400 4418 :issue => {
4401 4419 :project_id => '2', :tracker_id => '', :assigned_to_id => '2',
4402 4420 :status_id => '1', :start_date => '2009-12-01', :due_date => '2009-12-31'
4403 4421 }
4404 4422 end
4405 4423 end
4406 4424
4407 4425 copied_issues = Issue.where(:project_id => 2).limit(2).order('id desc').to_a
4408 4426 assert_equal 2, copied_issues.size
4409 4427 copied_issues.each do |issue|
4410 4428 assert_equal 2, issue.project_id, "Project is incorrect"
4411 4429 assert_equal 2, issue.assigned_to_id, "Assigned to is incorrect"
4412 4430 assert_equal 1, issue.status_id, "Status is incorrect"
4413 4431 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
4414 4432 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
4415 4433 end
4416 4434 end
4417 4435
4418 4436 def test_bulk_copy_should_allow_adding_a_note
4419 4437 @request.session[:user_id] = 2
4420 4438 assert_difference 'Issue.count', 1 do
4421 4439 post :bulk_update, :ids => [1], :copy => '1',
4422 4440 :notes => 'Copying one issue',
4423 4441 :issue => {
4424 4442 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
4425 4443 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
4426 4444 }
4427 4445 end
4428 4446 issue = Issue.order('id DESC').first
4429 4447 assert_equal 1, issue.journals.size
4430 4448 journal = issue.journals.first
4431 4449 assert_equal 'Copying one issue', journal.notes
4432 4450 end
4433 4451
4434 4452 def test_bulk_copy_should_allow_not_copying_the_attachments
4435 4453 attachment_count = Issue.find(3).attachments.size
4436 4454 assert attachment_count > 0
4437 4455 @request.session[:user_id] = 2
4438 4456
4439 4457 assert_difference 'Issue.count', 1 do
4440 4458 assert_no_difference 'Attachment.count' do
4441 4459 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '0',
4442 4460 :issue => {
4443 4461 :project_id => ''
4444 4462 }
4445 4463 end
4446 4464 end
4447 4465 end
4448 4466
4449 4467 def test_bulk_copy_should_allow_copying_the_attachments
4450 4468 attachment_count = Issue.find(3).attachments.size
4451 4469 assert attachment_count > 0
4452 4470 @request.session[:user_id] = 2
4453 4471
4454 4472 assert_difference 'Issue.count', 1 do
4455 4473 assert_difference 'Attachment.count', attachment_count do
4456 4474 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '1',
4457 4475 :issue => {
4458 4476 :project_id => ''
4459 4477 }
4460 4478 end
4461 4479 end
4462 4480 end
4463 4481
4464 4482 def test_bulk_copy_should_add_relations_with_copied_issues
4465 4483 @request.session[:user_id] = 2
4466 4484
4467 4485 assert_difference 'Issue.count', 2 do
4468 4486 assert_difference 'IssueRelation.count', 2 do
4469 4487 post :bulk_update, :ids => [1, 3], :copy => '1', :link_copy => '1',
4470 4488 :issue => {
4471 4489 :project_id => '1'
4472 4490 }
4473 4491 end
4474 4492 end
4475 4493 end
4476 4494
4477 4495 def test_bulk_copy_should_allow_not_copying_the_subtasks
4478 4496 issue = Issue.generate_with_descendants!
4479 4497 @request.session[:user_id] = 2
4480 4498
4481 4499 assert_difference 'Issue.count', 1 do
4482 4500 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '0',
4483 4501 :issue => {
4484 4502 :project_id => ''
4485 4503 }
4486 4504 end
4487 4505 end
4488 4506
4489 4507 def test_bulk_copy_should_allow_copying_the_subtasks
4490 4508 issue = Issue.generate_with_descendants!
4491 4509 count = issue.descendants.count
4492 4510 @request.session[:user_id] = 2
4493 4511
4494 4512 assert_difference 'Issue.count', count+1 do
4495 4513 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '1',
4496 4514 :issue => {
4497 4515 :project_id => ''
4498 4516 }
4499 4517 end
4500 4518 copy = Issue.where(:parent_id => nil).order("id DESC").first
4501 4519 assert_equal count, copy.descendants.count
4502 4520 end
4503 4521
4504 4522 def test_bulk_copy_should_not_copy_selected_subtasks_twice
4505 4523 issue = Issue.generate_with_descendants!
4506 4524 count = issue.descendants.count
4507 4525 @request.session[:user_id] = 2
4508 4526
4509 4527 assert_difference 'Issue.count', count+1 do
4510 4528 post :bulk_update, :ids => issue.self_and_descendants.map(&:id), :copy => '1', :copy_subtasks => '1',
4511 4529 :issue => {
4512 4530 :project_id => ''
4513 4531 }
4514 4532 end
4515 4533 copy = Issue.where(:parent_id => nil).order("id DESC").first
4516 4534 assert_equal count, copy.descendants.count
4517 4535 end
4518 4536
4519 4537 def test_bulk_copy_to_another_project_should_follow_when_needed
4520 4538 @request.session[:user_id] = 2
4521 4539 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
4522 4540 issue = Issue.order('id DESC').first
4523 4541 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
4524 4542 end
4525 4543
4526 4544 def test_bulk_copy_with_all_failures_should_display_errors
4527 4545 @request.session[:user_id] = 2
4528 4546 post :bulk_update, :ids => [1, 2], :copy => '1', :issue => {:start_date => 'foo'}
4529 4547
4530 4548 assert_response :success
4531 4549 end
4532 4550
4533 4551 def test_destroy_issue_with_no_time_entries
4534 4552 assert_nil TimeEntry.find_by_issue_id(2)
4535 4553 @request.session[:user_id] = 2
4536 4554
4537 4555 assert_difference 'Issue.count', -1 do
4538 4556 delete :destroy, :id => 2
4539 4557 end
4540 4558 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4541 4559 assert_nil Issue.find_by_id(2)
4542 4560 end
4543 4561
4544 4562 def test_destroy_issues_with_time_entries
4545 4563 @request.session[:user_id] = 2
4546 4564
4547 4565 assert_no_difference 'Issue.count' do
4548 4566 delete :destroy, :ids => [1, 3]
4549 4567 end
4550 4568 assert_response :success
4551 4569
4552 4570 assert_select 'form' do
4553 4571 assert_select 'input[name=_method][value=delete]'
4554 4572 end
4555 4573 end
4556 4574
4557 4575 def test_destroy_issues_and_destroy_time_entries
4558 4576 @request.session[:user_id] = 2
4559 4577
4560 4578 assert_difference 'Issue.count', -2 do
4561 4579 assert_difference 'TimeEntry.count', -3 do
4562 4580 delete :destroy, :ids => [1, 3], :todo => 'destroy'
4563 4581 end
4564 4582 end
4565 4583 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4566 4584 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4567 4585 assert_nil TimeEntry.find_by_id([1, 2])
4568 4586 end
4569 4587
4570 4588 def test_destroy_issues_and_assign_time_entries_to_project
4571 4589 @request.session[:user_id] = 2
4572 4590
4573 4591 assert_difference 'Issue.count', -2 do
4574 4592 assert_no_difference 'TimeEntry.count' do
4575 4593 delete :destroy, :ids => [1, 3], :todo => 'nullify'
4576 4594 end
4577 4595 end
4578 4596 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4579 4597 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4580 4598 assert_nil TimeEntry.find(1).issue_id
4581 4599 assert_nil TimeEntry.find(2).issue_id
4582 4600 end
4583 4601
4584 4602 def test_destroy_issues_and_reassign_time_entries_to_another_issue
4585 4603 @request.session[:user_id] = 2
4586 4604
4587 4605 assert_difference 'Issue.count', -2 do
4588 4606 assert_no_difference 'TimeEntry.count' do
4589 4607 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
4590 4608 end
4591 4609 end
4592 4610 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4593 4611 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4594 4612 assert_equal 2, TimeEntry.find(1).issue_id
4595 4613 assert_equal 2, TimeEntry.find(2).issue_id
4596 4614 end
4597 4615
4598 4616 def test_destroy_issues_and_reassign_time_entries_to_an_invalid_issue_should_fail
4599 4617 @request.session[:user_id] = 2
4600 4618
4601 4619 assert_no_difference 'Issue.count' do
4602 4620 assert_no_difference 'TimeEntry.count' do
4603 4621 # try to reassign time to an issue of another project
4604 4622 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 4
4605 4623 end
4606 4624 end
4607 4625 assert_response :success
4608 4626 end
4609 4627
4610 4628 def test_destroy_issues_from_different_projects
4611 4629 @request.session[:user_id] = 2
4612 4630
4613 4631 assert_difference 'Issue.count', -3 do
4614 4632 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
4615 4633 end
4616 4634 assert_redirected_to :controller => 'issues', :action => 'index'
4617 4635 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
4618 4636 end
4619 4637
4620 4638 def test_destroy_parent_and_child_issues
4621 4639 parent = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Parent Issue')
4622 4640 child = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Child Issue', :parent_issue_id => parent.id)
4623 4641 assert child.is_descendant_of?(parent.reload)
4624 4642
4625 4643 @request.session[:user_id] = 2
4626 4644 assert_difference 'Issue.count', -2 do
4627 4645 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
4628 4646 end
4629 4647 assert_response 302
4630 4648 end
4631 4649
4632 4650 def test_destroy_invalid_should_respond_with_404
4633 4651 @request.session[:user_id] = 2
4634 4652 assert_no_difference 'Issue.count' do
4635 4653 delete :destroy, :id => 999
4636 4654 end
4637 4655 assert_response 404
4638 4656 end
4639 4657
4640 4658 def test_destroy_with_permission_on_tracker_should_be_allowed
4641 4659 role = Role.find(1)
4642 4660 role.set_permission_trackers :delete_issues, [1]
4643 4661 role.save!
4644 4662 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
4645 4663
4646 4664 @request.session[:user_id] = 2
4647 4665 assert_difference 'Issue.count', -1 do
4648 4666 delete :destroy, :id => issue.id
4649 4667 end
4650 4668 assert_response 302
4651 4669 end
4652 4670
4653 4671 def test_destroy_without_permission_on_tracker_should_be_denied
4654 4672 role = Role.find(1)
4655 4673 role.set_permission_trackers :delete_issues, [2]
4656 4674 role.save!
4657 4675 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
4658 4676
4659 4677 @request.session[:user_id] = 2
4660 4678 assert_no_difference 'Issue.count' do
4661 4679 delete :destroy, :id => issue.id
4662 4680 end
4663 4681 assert_response 403
4664 4682 end
4665 4683
4666 4684 def test_default_search_scope
4667 4685 get :index
4668 4686
4669 4687 assert_select 'div#quick-search form' do
4670 4688 assert_select 'input[name=issues][value="1"][type=hidden]'
4671 4689 end
4672 4690 end
4673 4691
4674 4692 def setup_user_with_copy_but_not_add_permission
4675 4693 Role.all.each {|r| r.remove_permission! :add_issues}
4676 4694 Role.find_by_name('Manager').add_permission! :add_issues
4677 4695 user = User.generate!
4678 4696 User.add_to_project(user, Project.find(1), Role.find_by_name('Developer'))
4679 4697 User.add_to_project(user, Project.find(2), Role.find_by_name('Manager'))
4680 4698 user
4681 4699 end
4682 4700 end
General Comments 0
You need to be logged in to leave comments. Login now