##// 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 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class IssuesController < ApplicationController
18 class IssuesController < ApplicationController
19 default_search_scope :issues
19 default_search_scope :issues
20
20
21 before_action :find_issue, :only => [:show, :edit, :update]
21 before_action :find_issue, :only => [:show, :edit, :update]
22 before_action :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
22 before_action :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
23 before_action :authorize, :except => [:index, :new, :create]
23 before_action :authorize, :except => [:index, :new, :create]
24 before_action :find_optional_project, :only => [:index, :new, :create]
24 before_action :find_optional_project, :only => [:index, :new, :create]
25 before_action :build_new_issue_from_params, :only => [:new, :create]
25 before_action :build_new_issue_from_params, :only => [:new, :create]
26 accept_rss_auth :index, :show
26 accept_rss_auth :index, :show
27 accept_api_auth :index, :show, :create, :update, :destroy
27 accept_api_auth :index, :show, :create, :update, :destroy
28
28
29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
30
30
31 helper :journals
31 helper :journals
32 helper :projects
32 helper :projects
33 helper :custom_fields
33 helper :custom_fields
34 helper :issue_relations
34 helper :issue_relations
35 helper :watchers
35 helper :watchers
36 helper :attachments
36 helper :attachments
37 helper :queries
37 helper :queries
38 include QueriesHelper
38 include QueriesHelper
39 helper :repositories
39 helper :repositories
40 helper :sort
40 helper :sort
41 include SortHelper
41 include SortHelper
42 helper :timelog
42 helper :timelog
43
43
44 def index
44 def index
45 retrieve_query
45 retrieve_query
46 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
46 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
47 sort_update(@query.sortable_columns)
47 sort_update(@query.sortable_columns)
48 @query.sort_criteria = sort_criteria.to_a
48 @query.sort_criteria = sort_criteria.to_a
49
49
50 if @query.valid?
50 if @query.valid?
51 case params[:format]
51 case params[:format]
52 when 'csv', 'pdf'
52 when 'csv', 'pdf'
53 @limit = Setting.issues_export_limit.to_i
53 @limit = Setting.issues_export_limit.to_i
54 if params[:columns] == 'all'
54 if params[:columns] == 'all'
55 @query.column_names = @query.available_inline_columns.map(&:name)
55 @query.column_names = @query.available_inline_columns.map(&:name)
56 end
56 end
57 when 'atom'
57 when 'atom'
58 @limit = Setting.feeds_limit.to_i
58 @limit = Setting.feeds_limit.to_i
59 when 'xml', 'json'
59 when 'xml', 'json'
60 @offset, @limit = api_offset_and_limit
60 @offset, @limit = api_offset_and_limit
61 @query.column_names = %w(author)
61 @query.column_names = %w(author)
62 else
62 else
63 @limit = per_page_option
63 @limit = per_page_option
64 end
64 end
65
65
66 @issue_count = @query.issue_count
66 @issue_count = @query.issue_count
67 @issue_pages = Paginator.new @issue_count, @limit, params['page']
67 @issue_pages = Paginator.new @issue_count, @limit, params['page']
68 @offset ||= @issue_pages.offset
68 @offset ||= @issue_pages.offset
69 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
69 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
70 :order => sort_clause,
70 :order => sort_clause,
71 :offset => @offset,
71 :offset => @offset,
72 :limit => @limit)
72 :limit => @limit)
73 @issue_count_by_group = @query.issue_count_by_group
73 @issue_count_by_group = @query.issue_count_by_group
74
74
75 respond_to do |format|
75 respond_to do |format|
76 format.html { render :template => 'issues/index', :layout => !request.xhr? }
76 format.html { render :template => 'issues/index', :layout => !request.xhr? }
77 format.api {
77 format.api {
78 Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
78 Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
79 }
79 }
80 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
80 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
81 format.csv { send_data(query_to_csv(@issues, @query, params[:csv]), :type => 'text/csv; header=present', :filename => 'issues.csv') }
81 format.csv { send_data(query_to_csv(@issues, @query, params[:csv]), :type => 'text/csv; header=present', :filename => 'issues.csv') }
82 format.pdf { send_file_headers! :type => 'application/pdf', :filename => 'issues.pdf' }
82 format.pdf { send_file_headers! :type => 'application/pdf', :filename => 'issues.pdf' }
83 end
83 end
84 else
84 else
85 respond_to do |format|
85 respond_to do |format|
86 format.html { render(:template => 'issues/index', :layout => !request.xhr?) }
86 format.html { render(:template => 'issues/index', :layout => !request.xhr?) }
87 format.any(:atom, :csv, :pdf) { head 422 }
87 format.any(:atom, :csv, :pdf) { head 422 }
88 format.api { render_validation_errors(@query) }
88 format.api { render_validation_errors(@query) }
89 end
89 end
90 end
90 end
91 rescue ActiveRecord::RecordNotFound
91 rescue ActiveRecord::RecordNotFound
92 render_404
92 render_404
93 end
93 end
94
94
95 def show
95 def show
96 @journals = @issue.journals.
96 @journals = @issue.journals.
97 preload(:details).
97 preload(:details).
98 preload(:user => :email_address).
98 preload(:user => :email_address).
99 reorder(:created_on, :id).to_a
99 reorder(:created_on, :id).to_a
100 @journals.each_with_index {|j,i| j.indice = i+1}
100 @journals.each_with_index {|j,i| j.indice = i+1}
101 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
101 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
102 Journal.preload_journals_details_custom_fields(@journals)
102 Journal.preload_journals_details_custom_fields(@journals)
103 @journals.select! {|journal| journal.notes? || journal.visible_details.any?}
103 @journals.select! {|journal| journal.notes? || journal.visible_details.any?}
104 @journals.reverse! if User.current.wants_comments_in_reverse_order?
104 @journals.reverse! if User.current.wants_comments_in_reverse_order?
105
105
106 @changesets = @issue.changesets.visible.preload(:repository, :user).to_a
106 @changesets = @issue.changesets.visible.preload(:repository, :user).to_a
107 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
107 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
108
108
109 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
109 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
110 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
110 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
111 @priorities = IssuePriority.active
111 @priorities = IssuePriority.active
112 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
112 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
113 @relation = IssueRelation.new
113 @relation = IssueRelation.new
114
114
115 respond_to do |format|
115 respond_to do |format|
116 format.html {
116 format.html {
117 retrieve_previous_and_next_issue_ids
117 retrieve_previous_and_next_issue_ids
118 render :template => 'issues/show'
118 render :template => 'issues/show'
119 }
119 }
120 format.api
120 format.api
121 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
121 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
122 format.pdf {
122 format.pdf {
123 send_file_headers! :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf"
123 send_file_headers! :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf"
124 }
124 }
125 end
125 end
126 end
126 end
127
127
128 def new
128 def new
129 respond_to do |format|
129 respond_to do |format|
130 format.html { render :action => 'new', :layout => !request.xhr? }
130 format.html { render :action => 'new', :layout => !request.xhr? }
131 format.js
131 format.js
132 end
132 end
133 end
133 end
134
134
135 def create
135 def create
136 unless User.current.allowed_to?(:add_issues, @issue.project, :global => true)
136 unless User.current.allowed_to?(:add_issues, @issue.project, :global => true)
137 raise ::Unauthorized
137 raise ::Unauthorized
138 end
138 end
139 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
139 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
140 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
140 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
141 if @issue.save
141 if @issue.save
142 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
142 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
143 respond_to do |format|
143 respond_to do |format|
144 format.html {
144 format.html {
145 render_attachment_warning_if_needed(@issue)
145 render_attachment_warning_if_needed(@issue)
146 flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
146 flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
147 redirect_after_create
147 redirect_after_create
148 }
148 }
149 format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
149 format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
150 end
150 end
151 return
151 return
152 else
152 else
153 respond_to do |format|
153 respond_to do |format|
154 format.html {
154 format.html { render :action => 'new' }
155 if @issue.project.nil?
156 render_error :status => 422
157 else
158 render :action => 'new'
159 end
160 }
161 format.api { render_validation_errors(@issue) }
155 format.api { render_validation_errors(@issue) }
162 end
156 end
163 end
157 end
164 end
158 end
165
159
166 def edit
160 def edit
167 return unless update_issue_from_params
161 return unless update_issue_from_params
168
162
169 respond_to do |format|
163 respond_to do |format|
170 format.html { }
164 format.html { }
171 format.js
165 format.js
172 end
166 end
173 end
167 end
174
168
175 def update
169 def update
176 return unless update_issue_from_params
170 return unless update_issue_from_params
177 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
171 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
178 saved = false
172 saved = false
179 begin
173 begin
180 saved = save_issue_with_child_records
174 saved = save_issue_with_child_records
181 rescue ActiveRecord::StaleObjectError
175 rescue ActiveRecord::StaleObjectError
182 @conflict = true
176 @conflict = true
183 if params[:last_journal_id]
177 if params[:last_journal_id]
184 @conflict_journals = @issue.journals_after(params[:last_journal_id]).to_a
178 @conflict_journals = @issue.journals_after(params[:last_journal_id]).to_a
185 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
179 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
186 end
180 end
187 end
181 end
188
182
189 if saved
183 if saved
190 render_attachment_warning_if_needed(@issue)
184 render_attachment_warning_if_needed(@issue)
191 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
185 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
192
186
193 respond_to do |format|
187 respond_to do |format|
194 format.html { redirect_back_or_default issue_path(@issue, previous_and_next_issue_ids_params) }
188 format.html { redirect_back_or_default issue_path(@issue, previous_and_next_issue_ids_params) }
195 format.api { render_api_ok }
189 format.api { render_api_ok }
196 end
190 end
197 else
191 else
198 respond_to do |format|
192 respond_to do |format|
199 format.html { render :action => 'edit' }
193 format.html { render :action => 'edit' }
200 format.api { render_validation_errors(@issue) }
194 format.api { render_validation_errors(@issue) }
201 end
195 end
202 end
196 end
203 end
197 end
204
198
205 # Bulk edit/copy a set of issues
199 # Bulk edit/copy a set of issues
206 def bulk_edit
200 def bulk_edit
207 @issues.sort!
201 @issues.sort!
208 @copy = params[:copy].present?
202 @copy = params[:copy].present?
209 @notes = params[:notes]
203 @notes = params[:notes]
210
204
211 if @copy
205 if @copy
212 unless User.current.allowed_to?(:copy_issues, @projects)
206 unless User.current.allowed_to?(:copy_issues, @projects)
213 raise ::Unauthorized
207 raise ::Unauthorized
214 end
208 end
215 else
209 else
216 unless @issues.all?(&:attributes_editable?)
210 unless @issues.all?(&:attributes_editable?)
217 raise ::Unauthorized
211 raise ::Unauthorized
218 end
212 end
219 end
213 end
220
214
221 @allowed_projects = Issue.allowed_target_projects
215 @allowed_projects = Issue.allowed_target_projects
222 if params[:issue]
216 if params[:issue]
223 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
217 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
224 if @target_project
218 if @target_project
225 target_projects = [@target_project]
219 target_projects = [@target_project]
226 end
220 end
227 end
221 end
228 target_projects ||= @projects
222 target_projects ||= @projects
229
223
230 if @copy
224 if @copy
231 # Copied issues will get their default statuses
225 # Copied issues will get their default statuses
232 @available_statuses = []
226 @available_statuses = []
233 else
227 else
234 @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
228 @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
235 end
229 end
236 @custom_fields = @issues.map{|i|i.editable_custom_fields}.reduce(:&)
230 @custom_fields = @issues.map{|i|i.editable_custom_fields}.reduce(:&)
237 @assignables = target_projects.map(&:assignable_users).reduce(:&)
231 @assignables = target_projects.map(&:assignable_users).reduce(:&)
238 @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p) }.reduce(:&)
232 @trackers = target_projects.map {|p| Issue.allowed_target_trackers(p) }.reduce(:&)
239 @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
233 @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
240 @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
234 @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
241 if @copy
235 if @copy
242 @attachments_present = @issues.detect {|i| i.attachments.any?}.present?
236 @attachments_present = @issues.detect {|i| i.attachments.any?}.present?
243 @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
237 @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
244 end
238 end
245
239
246 @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
240 @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
247
241
248 @issue_params = params[:issue] || {}
242 @issue_params = params[:issue] || {}
249 @issue_params[:custom_field_values] ||= {}
243 @issue_params[:custom_field_values] ||= {}
250 end
244 end
251
245
252 def bulk_update
246 def bulk_update
253 @issues.sort!
247 @issues.sort!
254 @copy = params[:copy].present?
248 @copy = params[:copy].present?
255
249
256 attributes = parse_params_for_bulk_update(params[:issue])
250 attributes = parse_params_for_bulk_update(params[:issue])
257 copy_subtasks = (params[:copy_subtasks] == '1')
251 copy_subtasks = (params[:copy_subtasks] == '1')
258 copy_attachments = (params[:copy_attachments] == '1')
252 copy_attachments = (params[:copy_attachments] == '1')
259
253
260 if @copy
254 if @copy
261 unless User.current.allowed_to?(:copy_issues, @projects)
255 unless User.current.allowed_to?(:copy_issues, @projects)
262 raise ::Unauthorized
256 raise ::Unauthorized
263 end
257 end
264 target_projects = @projects
258 target_projects = @projects
265 if attributes['project_id'].present?
259 if attributes['project_id'].present?
266 target_projects = Project.where(:id => attributes['project_id']).to_a
260 target_projects = Project.where(:id => attributes['project_id']).to_a
267 end
261 end
268 unless User.current.allowed_to?(:add_issues, target_projects)
262 unless User.current.allowed_to?(:add_issues, target_projects)
269 raise ::Unauthorized
263 raise ::Unauthorized
270 end
264 end
271 else
265 else
272 unless @issues.all?(&:attributes_editable?)
266 unless @issues.all?(&:attributes_editable?)
273 raise ::Unauthorized
267 raise ::Unauthorized
274 end
268 end
275 end
269 end
276
270
277 unsaved_issues = []
271 unsaved_issues = []
278 saved_issues = []
272 saved_issues = []
279
273
280 if @copy && copy_subtasks
274 if @copy && copy_subtasks
281 # Descendant issues will be copied with the parent task
275 # Descendant issues will be copied with the parent task
282 # Don't copy them twice
276 # Don't copy them twice
283 @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
277 @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
284 end
278 end
285
279
286 @issues.each do |orig_issue|
280 @issues.each do |orig_issue|
287 orig_issue.reload
281 orig_issue.reload
288 if @copy
282 if @copy
289 issue = orig_issue.copy({},
283 issue = orig_issue.copy({},
290 :attachments => copy_attachments,
284 :attachments => copy_attachments,
291 :subtasks => copy_subtasks,
285 :subtasks => copy_subtasks,
292 :link => link_copy?(params[:link_copy])
286 :link => link_copy?(params[:link_copy])
293 )
287 )
294 else
288 else
295 issue = orig_issue
289 issue = orig_issue
296 end
290 end
297 journal = issue.init_journal(User.current, params[:notes])
291 journal = issue.init_journal(User.current, params[:notes])
298 issue.safe_attributes = attributes
292 issue.safe_attributes = attributes
299 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
293 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
300 if issue.save
294 if issue.save
301 saved_issues << issue
295 saved_issues << issue
302 else
296 else
303 unsaved_issues << orig_issue
297 unsaved_issues << orig_issue
304 end
298 end
305 end
299 end
306
300
307 if unsaved_issues.empty?
301 if unsaved_issues.empty?
308 flash[:notice] = l(:notice_successful_update) unless saved_issues.empty?
302 flash[:notice] = l(:notice_successful_update) unless saved_issues.empty?
309 if params[:follow]
303 if params[:follow]
310 if @issues.size == 1 && saved_issues.size == 1
304 if @issues.size == 1 && saved_issues.size == 1
311 redirect_to issue_path(saved_issues.first)
305 redirect_to issue_path(saved_issues.first)
312 elsif saved_issues.map(&:project).uniq.size == 1
306 elsif saved_issues.map(&:project).uniq.size == 1
313 redirect_to project_issues_path(saved_issues.map(&:project).first)
307 redirect_to project_issues_path(saved_issues.map(&:project).first)
314 end
308 end
315 else
309 else
316 redirect_back_or_default _project_issues_path(@project)
310 redirect_back_or_default _project_issues_path(@project)
317 end
311 end
318 else
312 else
319 @saved_issues = @issues
313 @saved_issues = @issues
320 @unsaved_issues = unsaved_issues
314 @unsaved_issues = unsaved_issues
321 @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a
315 @issues = Issue.visible.where(:id => @unsaved_issues.map(&:id)).to_a
322 bulk_edit
316 bulk_edit
323 render :action => 'bulk_edit'
317 render :action => 'bulk_edit'
324 end
318 end
325 end
319 end
326
320
327 def destroy
321 def destroy
328 raise Unauthorized unless @issues.all?(&:deletable?)
322 raise Unauthorized unless @issues.all?(&:deletable?)
329 @hours = TimeEntry.where(:issue_id => @issues.map(&:id)).sum(:hours).to_f
323 @hours = TimeEntry.where(:issue_id => @issues.map(&:id)).sum(:hours).to_f
330 if @hours > 0
324 if @hours > 0
331 case params[:todo]
325 case params[:todo]
332 when 'destroy'
326 when 'destroy'
333 # nothing to do
327 # nothing to do
334 when 'nullify'
328 when 'nullify'
335 TimeEntry.where(['issue_id IN (?)', @issues]).update_all('issue_id = NULL')
329 TimeEntry.where(['issue_id IN (?)', @issues]).update_all('issue_id = NULL')
336 when 'reassign'
330 when 'reassign'
337 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
331 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
338 if reassign_to.nil?
332 if reassign_to.nil?
339 flash.now[:error] = l(:error_issue_not_found_in_project)
333 flash.now[:error] = l(:error_issue_not_found_in_project)
340 return
334 return
341 else
335 else
342 TimeEntry.where(['issue_id IN (?)', @issues]).
336 TimeEntry.where(['issue_id IN (?)', @issues]).
343 update_all("issue_id = #{reassign_to.id}")
337 update_all("issue_id = #{reassign_to.id}")
344 end
338 end
345 else
339 else
346 # display the destroy form if it's a user request
340 # display the destroy form if it's a user request
347 return unless api_request?
341 return unless api_request?
348 end
342 end
349 end
343 end
350 @issues.each do |issue|
344 @issues.each do |issue|
351 begin
345 begin
352 issue.reload.destroy
346 issue.reload.destroy
353 rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
347 rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
354 # nothing to do, issue was already deleted (eg. by a parent)
348 # nothing to do, issue was already deleted (eg. by a parent)
355 end
349 end
356 end
350 end
357 respond_to do |format|
351 respond_to do |format|
358 format.html { redirect_back_or_default _project_issues_path(@project) }
352 format.html { redirect_back_or_default _project_issues_path(@project) }
359 format.api { render_api_ok }
353 format.api { render_api_ok }
360 end
354 end
361 end
355 end
362
356
363 # Overrides Redmine::MenuManager::MenuController::ClassMethods for
357 # Overrides Redmine::MenuManager::MenuController::ClassMethods for
364 # when the "New issue" tab is enabled
358 # when the "New issue" tab is enabled
365 def current_menu_item
359 def current_menu_item
366 if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym)
360 if Setting.new_item_menu_tab == '1' && [:new, :create].include?(action_name.to_sym)
367 :new_issue
361 :new_issue
368 else
362 else
369 super
363 super
370 end
364 end
371 end
365 end
372
366
373 private
367 private
374
368
375 def retrieve_previous_and_next_issue_ids
369 def retrieve_previous_and_next_issue_ids
376 if params[:prev_issue_id].present? || params[:next_issue_id].present?
370 if params[:prev_issue_id].present? || params[:next_issue_id].present?
377 @prev_issue_id = params[:prev_issue_id].presence.try(:to_i)
371 @prev_issue_id = params[:prev_issue_id].presence.try(:to_i)
378 @next_issue_id = params[:next_issue_id].presence.try(:to_i)
372 @next_issue_id = params[:next_issue_id].presence.try(:to_i)
379 @issue_position = params[:issue_position].presence.try(:to_i)
373 @issue_position = params[:issue_position].presence.try(:to_i)
380 @issue_count = params[:issue_count].presence.try(:to_i)
374 @issue_count = params[:issue_count].presence.try(:to_i)
381 else
375 else
382 retrieve_query_from_session
376 retrieve_query_from_session
383 if @query
377 if @query
384 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
378 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
385 sort_update(@query.sortable_columns, 'issues_index_sort')
379 sort_update(@query.sortable_columns, 'issues_index_sort')
386 limit = 500
380 limit = 500
387 issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])
381 issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])
388 if (idx = issue_ids.index(@issue.id)) && idx < limit
382 if (idx = issue_ids.index(@issue.id)) && idx < limit
389 if issue_ids.size < 500
383 if issue_ids.size < 500
390 @issue_position = idx + 1
384 @issue_position = idx + 1
391 @issue_count = issue_ids.size
385 @issue_count = issue_ids.size
392 end
386 end
393 @prev_issue_id = issue_ids[idx - 1] if idx > 0
387 @prev_issue_id = issue_ids[idx - 1] if idx > 0
394 @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
388 @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
395 end
389 end
396 end
390 end
397 end
391 end
398 end
392 end
399
393
400 def previous_and_next_issue_ids_params
394 def previous_and_next_issue_ids_params
401 {
395 {
402 :prev_issue_id => params[:prev_issue_id],
396 :prev_issue_id => params[:prev_issue_id],
403 :next_issue_id => params[:next_issue_id],
397 :next_issue_id => params[:next_issue_id],
404 :issue_position => params[:issue_position],
398 :issue_position => params[:issue_position],
405 :issue_count => params[:issue_count]
399 :issue_count => params[:issue_count]
406 }.reject {|k,v| k.blank?}
400 }.reject {|k,v| k.blank?}
407 end
401 end
408
402
409 # Used by #edit and #update to set some common instance variables
403 # Used by #edit and #update to set some common instance variables
410 # from the params
404 # from the params
411 def update_issue_from_params
405 def update_issue_from_params
412 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
406 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
413 if params[:time_entry]
407 if params[:time_entry]
414 @time_entry.safe_attributes = params[:time_entry]
408 @time_entry.safe_attributes = params[:time_entry]
415 end
409 end
416
410
417 @issue.init_journal(User.current)
411 @issue.init_journal(User.current)
418
412
419 issue_attributes = params[:issue]
413 issue_attributes = params[:issue]
420 if issue_attributes && params[:conflict_resolution]
414 if issue_attributes && params[:conflict_resolution]
421 case params[:conflict_resolution]
415 case params[:conflict_resolution]
422 when 'overwrite'
416 when 'overwrite'
423 issue_attributes = issue_attributes.dup
417 issue_attributes = issue_attributes.dup
424 issue_attributes.delete(:lock_version)
418 issue_attributes.delete(:lock_version)
425 when 'add_notes'
419 when 'add_notes'
426 issue_attributes = issue_attributes.slice(:notes, :private_notes)
420 issue_attributes = issue_attributes.slice(:notes, :private_notes)
427 when 'cancel'
421 when 'cancel'
428 redirect_to issue_path(@issue)
422 redirect_to issue_path(@issue)
429 return false
423 return false
430 end
424 end
431 end
425 end
432 @issue.safe_attributes = issue_attributes
426 @issue.safe_attributes = issue_attributes
433 @priorities = IssuePriority.active
427 @priorities = IssuePriority.active
434 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
428 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
435 true
429 true
436 end
430 end
437
431
438 # Used by #new and #create to build a new issue from the params
432 # Used by #new and #create to build a new issue from the params
439 # The new issue will be copied from an existing one if copy_from parameter is given
433 # The new issue will be copied from an existing one if copy_from parameter is given
440 def build_new_issue_from_params
434 def build_new_issue_from_params
441 @issue = Issue.new
435 @issue = Issue.new
442 if params[:copy_from]
436 if params[:copy_from]
443 begin
437 begin
444 @issue.init_journal(User.current)
438 @issue.init_journal(User.current)
445 @copy_from = Issue.visible.find(params[:copy_from])
439 @copy_from = Issue.visible.find(params[:copy_from])
446 unless User.current.allowed_to?(:copy_issues, @copy_from.project)
440 unless User.current.allowed_to?(:copy_issues, @copy_from.project)
447 raise ::Unauthorized
441 raise ::Unauthorized
448 end
442 end
449 @link_copy = link_copy?(params[:link_copy]) || request.get?
443 @link_copy = link_copy?(params[:link_copy]) || request.get?
450 @copy_attachments = params[:copy_attachments].present? || request.get?
444 @copy_attachments = params[:copy_attachments].present? || request.get?
451 @copy_subtasks = params[:copy_subtasks].present? || request.get?
445 @copy_subtasks = params[:copy_subtasks].present? || request.get?
452 @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks, :link => @link_copy)
446 @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks, :link => @link_copy)
453 @issue.parent_issue_id = @copy_from.parent_id
447 @issue.parent_issue_id = @copy_from.parent_id
454 rescue ActiveRecord::RecordNotFound
448 rescue ActiveRecord::RecordNotFound
455 render_404
449 render_404
456 return
450 return
457 end
451 end
458 end
452 end
459 @issue.project = @project
453 @issue.project = @project
460 if request.get?
454 if request.get?
461 @issue.project ||= @issue.allowed_target_projects.first
455 @issue.project ||= @issue.allowed_target_projects.first
462 end
456 end
463 @issue.author ||= User.current
457 @issue.author ||= User.current
464 @issue.start_date ||= User.current.today if Setting.default_issue_start_date_to_creation_date?
458 @issue.start_date ||= User.current.today if Setting.default_issue_start_date_to_creation_date?
465
459
466 attrs = (params[:issue] || {}).deep_dup
460 attrs = (params[:issue] || {}).deep_dup
467 if action_name == 'new' && params[:was_default_status] == attrs[:status_id]
461 if action_name == 'new' && params[:was_default_status] == attrs[:status_id]
468 attrs.delete(:status_id)
462 attrs.delete(:status_id)
469 end
463 end
470 if action_name == 'new' && params[:form_update_triggered_by] == 'issue_project_id'
464 if action_name == 'new' && params[:form_update_triggered_by] == 'issue_project_id'
471 # Discard submitted version when changing the project on the issue form
465 # Discard submitted version when changing the project on the issue form
472 # so we can use the default version for the new project
466 # so we can use the default version for the new project
473 attrs.delete(:fixed_version_id)
467 attrs.delete(:fixed_version_id)
474 end
468 end
475 @issue.safe_attributes = attrs
469 @issue.safe_attributes = attrs
476
470
477 if @issue.project
471 if @issue.project
478 @issue.tracker ||= @issue.allowed_target_trackers.first
472 @issue.tracker ||= @issue.allowed_target_trackers.first
479 if @issue.tracker.nil?
473 if @issue.tracker.nil?
480 if @issue.project.trackers.any?
474 if @issue.project.trackers.any?
481 # None of the project trackers is allowed to the user
475 # None of the project trackers is allowed to the user
482 render_error :message => l(:error_no_tracker_allowed_for_new_issue_in_project), :status => 403
476 render_error :message => l(:error_no_tracker_allowed_for_new_issue_in_project), :status => 403
483 else
477 else
484 # Project has no trackers
478 # Project has no trackers
485 render_error l(:error_no_tracker_in_project)
479 render_error l(:error_no_tracker_in_project)
486 end
480 end
487 return false
481 return false
488 end
482 end
489 if @issue.status.nil?
483 if @issue.status.nil?
490 render_error l(:error_no_default_issue_status)
484 render_error l(:error_no_default_issue_status)
491 return false
485 return false
492 end
486 end
487 else
488 render_error :message => l(:error_no_projects_with_tracker_allowed_for_new_issue), :status => 403
489 return false
493 end
490 end
494
491
495 @priorities = IssuePriority.active
492 @priorities = IssuePriority.active
496 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
493 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
497 end
494 end
498
495
499 # Saves @issue and a time_entry from the parameters
496 # Saves @issue and a time_entry from the parameters
500 def save_issue_with_child_records
497 def save_issue_with_child_records
501 Issue.transaction do
498 Issue.transaction do
502 if params[:time_entry] && (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) && User.current.allowed_to?(:log_time, @issue.project)
499 if params[:time_entry] && (params[:time_entry][:hours].present? || params[:time_entry][:comments].present?) && User.current.allowed_to?(:log_time, @issue.project)
503 time_entry = @time_entry || TimeEntry.new
500 time_entry = @time_entry || TimeEntry.new
504 time_entry.project = @issue.project
501 time_entry.project = @issue.project
505 time_entry.issue = @issue
502 time_entry.issue = @issue
506 time_entry.user = User.current
503 time_entry.user = User.current
507 time_entry.spent_on = User.current.today
504 time_entry.spent_on = User.current.today
508 time_entry.attributes = params[:time_entry]
505 time_entry.attributes = params[:time_entry]
509 @issue.time_entries << time_entry
506 @issue.time_entries << time_entry
510 end
507 end
511
508
512 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
509 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
513 if @issue.save
510 if @issue.save
514 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
511 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => time_entry, :journal => @issue.current_journal})
515 else
512 else
516 raise ActiveRecord::Rollback
513 raise ActiveRecord::Rollback
517 end
514 end
518 end
515 end
519 end
516 end
520
517
521 # Returns true if the issue copy should be linked
518 # Returns true if the issue copy should be linked
522 # to the original issue
519 # to the original issue
523 def link_copy?(param)
520 def link_copy?(param)
524 case Setting.link_copied_issue
521 case Setting.link_copied_issue
525 when 'yes'
522 when 'yes'
526 true
523 true
527 when 'no'
524 when 'no'
528 false
525 false
529 when 'ask'
526 when 'ask'
530 param == '1'
527 param == '1'
531 end
528 end
532 end
529 end
533
530
534 # Redirects user after a successful issue creation
531 # Redirects user after a successful issue creation
535 def redirect_after_create
532 def redirect_after_create
536 if params[:continue]
533 if params[:continue]
537 attrs = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?}
534 attrs = {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?}
538 if params[:project_id]
535 if params[:project_id]
539 redirect_to new_project_issue_path(@issue.project, :issue => attrs)
536 redirect_to new_project_issue_path(@issue.project, :issue => attrs)
540 else
537 else
541 attrs.merge! :project_id => @issue.project_id
538 attrs.merge! :project_id => @issue.project_id
542 redirect_to new_issue_path(:issue => attrs)
539 redirect_to new_issue_path(:issue => attrs)
543 end
540 end
544 else
541 else
545 redirect_to issue_path(@issue)
542 redirect_to issue_path(@issue)
546 end
543 end
547 end
544 end
548 end
545 end
@@ -1,1193 +1,1194
1 en:
1 en:
2 # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl)
2 # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl)
3 direction: ltr
3 direction: ltr
4 date:
4 date:
5 formats:
5 formats:
6 # Use the strftime parameters for formats.
6 # Use the strftime parameters for formats.
7 # When no format has been given, it uses default.
7 # When no format has been given, it uses default.
8 # You can provide other formats here if you like!
8 # You can provide other formats here if you like!
9 default: "%m/%d/%Y"
9 default: "%m/%d/%Y"
10 short: "%b %d"
10 short: "%b %d"
11 long: "%B %d, %Y"
11 long: "%B %d, %Y"
12
12
13 day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
13 day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
14 abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
14 abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
15
15
16 # Don't forget the nil at the beginning; there's no such thing as a 0th month
16 # Don't forget the nil at the beginning; there's no such thing as a 0th month
17 month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
17 month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
18 abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
18 abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
19 # Used in date_select and datime_select.
19 # Used in date_select and datime_select.
20 order:
20 order:
21 - :year
21 - :year
22 - :month
22 - :month
23 - :day
23 - :day
24
24
25 time:
25 time:
26 formats:
26 formats:
27 default: "%m/%d/%Y %I:%M %p"
27 default: "%m/%d/%Y %I:%M %p"
28 time: "%I:%M %p"
28 time: "%I:%M %p"
29 short: "%d %b %H:%M"
29 short: "%d %b %H:%M"
30 long: "%B %d, %Y %H:%M"
30 long: "%B %d, %Y %H:%M"
31 am: "am"
31 am: "am"
32 pm: "pm"
32 pm: "pm"
33
33
34 datetime:
34 datetime:
35 distance_in_words:
35 distance_in_words:
36 half_a_minute: "half a minute"
36 half_a_minute: "half a minute"
37 less_than_x_seconds:
37 less_than_x_seconds:
38 one: "less than 1 second"
38 one: "less than 1 second"
39 other: "less than %{count} seconds"
39 other: "less than %{count} seconds"
40 x_seconds:
40 x_seconds:
41 one: "1 second"
41 one: "1 second"
42 other: "%{count} seconds"
42 other: "%{count} seconds"
43 less_than_x_minutes:
43 less_than_x_minutes:
44 one: "less than a minute"
44 one: "less than a minute"
45 other: "less than %{count} minutes"
45 other: "less than %{count} minutes"
46 x_minutes:
46 x_minutes:
47 one: "1 minute"
47 one: "1 minute"
48 other: "%{count} minutes"
48 other: "%{count} minutes"
49 about_x_hours:
49 about_x_hours:
50 one: "about 1 hour"
50 one: "about 1 hour"
51 other: "about %{count} hours"
51 other: "about %{count} hours"
52 x_hours:
52 x_hours:
53 one: "1 hour"
53 one: "1 hour"
54 other: "%{count} hours"
54 other: "%{count} hours"
55 x_days:
55 x_days:
56 one: "1 day"
56 one: "1 day"
57 other: "%{count} days"
57 other: "%{count} days"
58 about_x_months:
58 about_x_months:
59 one: "about 1 month"
59 one: "about 1 month"
60 other: "about %{count} months"
60 other: "about %{count} months"
61 x_months:
61 x_months:
62 one: "1 month"
62 one: "1 month"
63 other: "%{count} months"
63 other: "%{count} months"
64 about_x_years:
64 about_x_years:
65 one: "about 1 year"
65 one: "about 1 year"
66 other: "about %{count} years"
66 other: "about %{count} years"
67 over_x_years:
67 over_x_years:
68 one: "over 1 year"
68 one: "over 1 year"
69 other: "over %{count} years"
69 other: "over %{count} years"
70 almost_x_years:
70 almost_x_years:
71 one: "almost 1 year"
71 one: "almost 1 year"
72 other: "almost %{count} years"
72 other: "almost %{count} years"
73
73
74 number:
74 number:
75 format:
75 format:
76 separator: "."
76 separator: "."
77 delimiter: ""
77 delimiter: ""
78 precision: 3
78 precision: 3
79
79
80 human:
80 human:
81 format:
81 format:
82 delimiter: ""
82 delimiter: ""
83 precision: 3
83 precision: 3
84 storage_units:
84 storage_units:
85 format: "%n %u"
85 format: "%n %u"
86 units:
86 units:
87 byte:
87 byte:
88 one: "Byte"
88 one: "Byte"
89 other: "Bytes"
89 other: "Bytes"
90 kb: "KB"
90 kb: "KB"
91 mb: "MB"
91 mb: "MB"
92 gb: "GB"
92 gb: "GB"
93 tb: "TB"
93 tb: "TB"
94
94
95 # Used in array.to_sentence.
95 # Used in array.to_sentence.
96 support:
96 support:
97 array:
97 array:
98 sentence_connector: "and"
98 sentence_connector: "and"
99 skip_last_comma: false
99 skip_last_comma: false
100
100
101 activerecord:
101 activerecord:
102 errors:
102 errors:
103 template:
103 template:
104 header:
104 header:
105 one: "1 error prohibited this %{model} from being saved"
105 one: "1 error prohibited this %{model} from being saved"
106 other: "%{count} errors prohibited this %{model} from being saved"
106 other: "%{count} errors prohibited this %{model} from being saved"
107 messages:
107 messages:
108 inclusion: "is not included in the list"
108 inclusion: "is not included in the list"
109 exclusion: "is reserved"
109 exclusion: "is reserved"
110 invalid: "is invalid"
110 invalid: "is invalid"
111 confirmation: "doesn't match confirmation"
111 confirmation: "doesn't match confirmation"
112 accepted: "must be accepted"
112 accepted: "must be accepted"
113 empty: "cannot be empty"
113 empty: "cannot be empty"
114 blank: "cannot be blank"
114 blank: "cannot be blank"
115 too_long: "is too long (maximum is %{count} characters)"
115 too_long: "is too long (maximum is %{count} characters)"
116 too_short: "is too short (minimum is %{count} characters)"
116 too_short: "is too short (minimum is %{count} characters)"
117 wrong_length: "is the wrong length (should be %{count} characters)"
117 wrong_length: "is the wrong length (should be %{count} characters)"
118 taken: "has already been taken"
118 taken: "has already been taken"
119 not_a_number: "is not a number"
119 not_a_number: "is not a number"
120 not_a_date: "is not a valid date"
120 not_a_date: "is not a valid date"
121 greater_than: "must be greater than %{count}"
121 greater_than: "must be greater than %{count}"
122 greater_than_or_equal_to: "must be greater than or equal to %{count}"
122 greater_than_or_equal_to: "must be greater than or equal to %{count}"
123 equal_to: "must be equal to %{count}"
123 equal_to: "must be equal to %{count}"
124 less_than: "must be less than %{count}"
124 less_than: "must be less than %{count}"
125 less_than_or_equal_to: "must be less than or equal to %{count}"
125 less_than_or_equal_to: "must be less than or equal to %{count}"
126 odd: "must be odd"
126 odd: "must be odd"
127 even: "must be even"
127 even: "must be even"
128 greater_than_start_date: "must be greater than start date"
128 greater_than_start_date: "must be greater than start date"
129 not_same_project: "doesn't belong to the same project"
129 not_same_project: "doesn't belong to the same project"
130 circular_dependency: "This relation would create a circular dependency"
130 circular_dependency: "This relation would create a circular dependency"
131 cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks"
131 cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks"
132 earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
132 earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues"
133
133
134 actionview_instancetag_blank_option: Please select
134 actionview_instancetag_blank_option: Please select
135
135
136 general_text_No: 'No'
136 general_text_No: 'No'
137 general_text_Yes: 'Yes'
137 general_text_Yes: 'Yes'
138 general_text_no: 'no'
138 general_text_no: 'no'
139 general_text_yes: 'yes'
139 general_text_yes: 'yes'
140 general_lang_name: 'English'
140 general_lang_name: 'English'
141 general_csv_separator: ','
141 general_csv_separator: ','
142 general_csv_decimal_separator: '.'
142 general_csv_decimal_separator: '.'
143 general_csv_encoding: ISO-8859-1
143 general_csv_encoding: ISO-8859-1
144 general_pdf_fontname: freesans
144 general_pdf_fontname: freesans
145 general_pdf_monospaced_fontname: freemono
145 general_pdf_monospaced_fontname: freemono
146 general_first_day_of_week: '7'
146 general_first_day_of_week: '7'
147
147
148 notice_account_updated: Account was successfully updated.
148 notice_account_updated: Account was successfully updated.
149 notice_account_invalid_credentials: Invalid user or password
149 notice_account_invalid_credentials: Invalid user or password
150 notice_account_password_updated: Password was successfully updated.
150 notice_account_password_updated: Password was successfully updated.
151 notice_account_wrong_password: Wrong password
151 notice_account_wrong_password: Wrong password
152 notice_account_register_done: Account was successfully created. An email containing the instructions to activate your account was sent to %{email}.
152 notice_account_register_done: Account was successfully created. An email containing the instructions to activate your account was sent to %{email}.
153 notice_account_unknown_email: Unknown user.
153 notice_account_unknown_email: Unknown user.
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>.
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 notice_account_locked: Your account is locked.
155 notice_account_locked: Your account is locked.
156 notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
156 notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
157 notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
157 notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
158 notice_account_activated: Your account has been activated. You can now log in.
158 notice_account_activated: Your account has been activated. You can now log in.
159 notice_successful_create: Successful creation.
159 notice_successful_create: Successful creation.
160 notice_successful_update: Successful update.
160 notice_successful_update: Successful update.
161 notice_successful_delete: Successful deletion.
161 notice_successful_delete: Successful deletion.
162 notice_successful_connection: Successful connection.
162 notice_successful_connection: Successful connection.
163 notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
163 notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
164 notice_locking_conflict: Data has been updated by another user.
164 notice_locking_conflict: Data has been updated by another user.
165 notice_not_authorized: You are not authorized to access this page.
165 notice_not_authorized: You are not authorized to access this page.
166 notice_not_authorized_archived_project: The project you're trying to access has been archived.
166 notice_not_authorized_archived_project: The project you're trying to access has been archived.
167 notice_email_sent: "An email was sent to %{value}"
167 notice_email_sent: "An email was sent to %{value}"
168 notice_email_error: "An error occurred while sending mail (%{value})"
168 notice_email_error: "An error occurred while sending mail (%{value})"
169 notice_feeds_access_key_reseted: Your Atom access key was reset.
169 notice_feeds_access_key_reseted: Your Atom access key was reset.
170 notice_api_access_key_reseted: Your API access key was reset.
170 notice_api_access_key_reseted: Your API access key was reset.
171 notice_failed_to_save_issues: "Failed to save %{count} issue(s) on %{total} selected: %{ids}."
171 notice_failed_to_save_issues: "Failed to save %{count} issue(s) on %{total} selected: %{ids}."
172 notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}."
172 notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}."
173 notice_failed_to_save_members: "Failed to save member(s): %{errors}."
173 notice_failed_to_save_members: "Failed to save member(s): %{errors}."
174 notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
174 notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
175 notice_account_pending: "Your account was created and is now pending administrator approval."
175 notice_account_pending: "Your account was created and is now pending administrator approval."
176 notice_default_data_loaded: Default configuration successfully loaded.
176 notice_default_data_loaded: Default configuration successfully loaded.
177 notice_unable_delete_version: Unable to delete version.
177 notice_unable_delete_version: Unable to delete version.
178 notice_unable_delete_time_entry: Unable to delete time log entry.
178 notice_unable_delete_time_entry: Unable to delete time log entry.
179 notice_issue_done_ratios_updated: Issue done ratios updated.
179 notice_issue_done_ratios_updated: Issue done ratios updated.
180 notice_gantt_chart_truncated: "The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})"
180 notice_gantt_chart_truncated: "The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})"
181 notice_issue_successful_create: "Issue %{id} created."
181 notice_issue_successful_create: "Issue %{id} created."
182 notice_issue_update_conflict: "The issue has been updated by an other user while you were editing it."
182 notice_issue_update_conflict: "The issue has been updated by an other user while you were editing it."
183 notice_account_deleted: "Your account has been permanently deleted."
183 notice_account_deleted: "Your account has been permanently deleted."
184 notice_user_successful_create: "User %{id} created."
184 notice_user_successful_create: "User %{id} created."
185 notice_new_password_must_be_different: The new password must be different from the current password
185 notice_new_password_must_be_different: The new password must be different from the current password
186 notice_import_finished: "%{count} items have been imported"
186 notice_import_finished: "%{count} items have been imported"
187 notice_import_finished_with_errors: "%{count} out of %{total} items could not be imported"
187 notice_import_finished_with_errors: "%{count} out of %{total} items could not be imported"
188
188
189 error_can_t_load_default_data: "Default configuration could not be loaded: %{value}"
189 error_can_t_load_default_data: "Default configuration could not be loaded: %{value}"
190 error_scm_not_found: "The entry or revision was not found in the repository."
190 error_scm_not_found: "The entry or revision was not found in the repository."
191 error_scm_command_failed: "An error occurred when trying to access the repository: %{value}"
191 error_scm_command_failed: "An error occurred when trying to access the repository: %{value}"
192 error_scm_annotate: "The entry does not exist or cannot be annotated."
192 error_scm_annotate: "The entry does not exist or cannot be annotated."
193 error_scm_annotate_big_text_file: "The entry cannot be annotated, as it exceeds the maximum text file size."
193 error_scm_annotate_big_text_file: "The entry cannot be annotated, as it exceeds the maximum text file size."
194 error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
194 error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
195 error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.'
195 error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.'
196 error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
196 error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
197 error_can_not_delete_custom_field: Unable to delete custom field
197 error_can_not_delete_custom_field: Unable to delete custom field
198 error_can_not_delete_tracker: "This tracker contains issues and cannot be deleted."
198 error_can_not_delete_tracker: "This tracker contains issues and cannot be deleted."
199 error_can_not_remove_role: "This role is in use and cannot be deleted."
199 error_can_not_remove_role: "This role is in use and cannot be deleted."
200 error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version cannot be reopened'
200 error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version cannot be reopened'
201 error_can_not_archive_project: This project cannot be archived
201 error_can_not_archive_project: This project cannot be archived
202 error_issue_done_ratios_not_updated: "Issue done ratios not updated."
202 error_issue_done_ratios_not_updated: "Issue done ratios not updated."
203 error_workflow_copy_source: 'Please select a source tracker or role'
203 error_workflow_copy_source: 'Please select a source tracker or role'
204 error_workflow_copy_target: 'Please select target tracker(s) and role(s)'
204 error_workflow_copy_target: 'Please select target tracker(s) and role(s)'
205 error_unable_delete_issue_status: 'Unable to delete issue status'
205 error_unable_delete_issue_status: 'Unable to delete issue status'
206 error_unable_to_connect: "Unable to connect (%{value})"
206 error_unable_to_connect: "Unable to connect (%{value})"
207 error_attachment_too_big: "This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size})"
207 error_attachment_too_big: "This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size})"
208 error_session_expired: "Your session has expired. Please login again."
208 error_session_expired: "Your session has expired. Please login again."
209 warning_attachments_not_saved: "%{count} file(s) could not be saved."
209 warning_attachments_not_saved: "%{count} file(s) could not be saved."
210 error_password_expired: "Your password has expired or the administrator requires you to change it."
210 error_password_expired: "Your password has expired or the administrator requires you to change it."
211 error_invalid_file_encoding: "The file is not a valid %{encoding} encoded file"
211 error_invalid_file_encoding: "The file is not a valid %{encoding} encoded file"
212 error_invalid_csv_file_or_settings: "The file is not a CSV file or does not match the settings below"
212 error_invalid_csv_file_or_settings: "The file is not a CSV file or does not match the settings below"
213 error_can_not_read_import_file: "An error occurred while reading the file to import"
213 error_can_not_read_import_file: "An error occurred while reading the file to import"
214 error_attachment_extension_not_allowed: "Attachment extension %{extension} is not allowed"
214 error_attachment_extension_not_allowed: "Attachment extension %{extension} is not allowed"
215 error_ldap_bind_credentials: "Invalid LDAP Account/Password"
215 error_ldap_bind_credentials: "Invalid LDAP Account/Password"
216 error_no_tracker_allowed_for_new_issue_in_project: "The project doesn't have any trackers for which you can create an issue"
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 mail_subject_lost_password: "Your %{value} password"
219 mail_subject_lost_password: "Your %{value} password"
219 mail_body_lost_password: 'To change your password, click on the following link:'
220 mail_body_lost_password: 'To change your password, click on the following link:'
220 mail_subject_register: "Your %{value} account activation"
221 mail_subject_register: "Your %{value} account activation"
221 mail_body_register: 'To activate your account, click on the following link:'
222 mail_body_register: 'To activate your account, click on the following link:'
222 mail_body_account_information_external: "You can use your %{value} account to log in."
223 mail_body_account_information_external: "You can use your %{value} account to log in."
223 mail_body_account_information: Your account information
224 mail_body_account_information: Your account information
224 mail_subject_account_activation_request: "%{value} account activation request"
225 mail_subject_account_activation_request: "%{value} account activation request"
225 mail_body_account_activation_request: "A new user (%{value}) has registered. The account is pending your approval:"
226 mail_body_account_activation_request: "A new user (%{value}) has registered. The account is pending your approval:"
226 mail_subject_reminder: "%{count} issue(s) due in the next %{days} days"
227 mail_subject_reminder: "%{count} issue(s) due in the next %{days} days"
227 mail_body_reminder: "%{count} issue(s) that are assigned to you are due in the next %{days} days:"
228 mail_body_reminder: "%{count} issue(s) that are assigned to you are due in the next %{days} days:"
228 mail_subject_wiki_content_added: "'%{id}' wiki page has been added"
229 mail_subject_wiki_content_added: "'%{id}' wiki page has been added"
229 mail_body_wiki_content_added: "The '%{id}' wiki page has been added by %{author}."
230 mail_body_wiki_content_added: "The '%{id}' wiki page has been added by %{author}."
230 mail_subject_wiki_content_updated: "'%{id}' wiki page has been updated"
231 mail_subject_wiki_content_updated: "'%{id}' wiki page has been updated"
231 mail_body_wiki_content_updated: "The '%{id}' wiki page has been updated by %{author}."
232 mail_body_wiki_content_updated: "The '%{id}' wiki page has been updated by %{author}."
232 mail_subject_security_notification: "Security notification"
233 mail_subject_security_notification: "Security notification"
233 mail_body_security_notification_change: "%{field} was changed."
234 mail_body_security_notification_change: "%{field} was changed."
234 mail_body_security_notification_change_to: "%{field} was changed to %{value}."
235 mail_body_security_notification_change_to: "%{field} was changed to %{value}."
235 mail_body_security_notification_add: "%{field} %{value} was added."
236 mail_body_security_notification_add: "%{field} %{value} was added."
236 mail_body_security_notification_remove: "%{field} %{value} was removed."
237 mail_body_security_notification_remove: "%{field} %{value} was removed."
237 mail_body_security_notification_notify_enabled: "Email address %{value} now receives notifications."
238 mail_body_security_notification_notify_enabled: "Email address %{value} now receives notifications."
238 mail_body_security_notification_notify_disabled: "Email address %{value} no longer receives notifications."
239 mail_body_security_notification_notify_disabled: "Email address %{value} no longer receives notifications."
239 mail_body_settings_updated: "The following settings were changed:"
240 mail_body_settings_updated: "The following settings were changed:"
240 mail_body_password_updated: "Your password has been changed."
241 mail_body_password_updated: "Your password has been changed."
241
242
242 field_name: Name
243 field_name: Name
243 field_description: Description
244 field_description: Description
244 field_summary: Summary
245 field_summary: Summary
245 field_is_required: Required
246 field_is_required: Required
246 field_firstname: First name
247 field_firstname: First name
247 field_lastname: Last name
248 field_lastname: Last name
248 field_mail: Email
249 field_mail: Email
249 field_address: Email
250 field_address: Email
250 field_filename: File
251 field_filename: File
251 field_filesize: Size
252 field_filesize: Size
252 field_downloads: Downloads
253 field_downloads: Downloads
253 field_author: Author
254 field_author: Author
254 field_created_on: Created
255 field_created_on: Created
255 field_updated_on: Updated
256 field_updated_on: Updated
256 field_closed_on: Closed
257 field_closed_on: Closed
257 field_field_format: Format
258 field_field_format: Format
258 field_is_for_all: For all projects
259 field_is_for_all: For all projects
259 field_possible_values: Possible values
260 field_possible_values: Possible values
260 field_regexp: Regular expression
261 field_regexp: Regular expression
261 field_min_length: Minimum length
262 field_min_length: Minimum length
262 field_max_length: Maximum length
263 field_max_length: Maximum length
263 field_value: Value
264 field_value: Value
264 field_category: Category
265 field_category: Category
265 field_title: Title
266 field_title: Title
266 field_project: Project
267 field_project: Project
267 field_issue: Issue
268 field_issue: Issue
268 field_status: Status
269 field_status: Status
269 field_notes: Notes
270 field_notes: Notes
270 field_is_closed: Issue closed
271 field_is_closed: Issue closed
271 field_is_default: Default value
272 field_is_default: Default value
272 field_tracker: Tracker
273 field_tracker: Tracker
273 field_subject: Subject
274 field_subject: Subject
274 field_due_date: Due date
275 field_due_date: Due date
275 field_assigned_to: Assignee
276 field_assigned_to: Assignee
276 field_priority: Priority
277 field_priority: Priority
277 field_fixed_version: Target version
278 field_fixed_version: Target version
278 field_user: User
279 field_user: User
279 field_principal: Principal
280 field_principal: Principal
280 field_role: Role
281 field_role: Role
281 field_homepage: Homepage
282 field_homepage: Homepage
282 field_is_public: Public
283 field_is_public: Public
283 field_parent: Subproject of
284 field_parent: Subproject of
284 field_is_in_roadmap: Issues displayed in roadmap
285 field_is_in_roadmap: Issues displayed in roadmap
285 field_login: Login
286 field_login: Login
286 field_mail_notification: Email notifications
287 field_mail_notification: Email notifications
287 field_admin: Administrator
288 field_admin: Administrator
288 field_last_login_on: Last connection
289 field_last_login_on: Last connection
289 field_language: Language
290 field_language: Language
290 field_effective_date: Due date
291 field_effective_date: Due date
291 field_password: Password
292 field_password: Password
292 field_new_password: New password
293 field_new_password: New password
293 field_password_confirmation: Confirmation
294 field_password_confirmation: Confirmation
294 field_version: Version
295 field_version: Version
295 field_type: Type
296 field_type: Type
296 field_host: Host
297 field_host: Host
297 field_port: Port
298 field_port: Port
298 field_account: Account
299 field_account: Account
299 field_base_dn: Base DN
300 field_base_dn: Base DN
300 field_attr_login: Login attribute
301 field_attr_login: Login attribute
301 field_attr_firstname: Firstname attribute
302 field_attr_firstname: Firstname attribute
302 field_attr_lastname: Lastname attribute
303 field_attr_lastname: Lastname attribute
303 field_attr_mail: Email attribute
304 field_attr_mail: Email attribute
304 field_onthefly: On-the-fly user creation
305 field_onthefly: On-the-fly user creation
305 field_start_date: Start date
306 field_start_date: Start date
306 field_done_ratio: "% Done"
307 field_done_ratio: "% Done"
307 field_auth_source: Authentication mode
308 field_auth_source: Authentication mode
308 field_hide_mail: Hide my email address
309 field_hide_mail: Hide my email address
309 field_comments: Comment
310 field_comments: Comment
310 field_url: URL
311 field_url: URL
311 field_start_page: Start page
312 field_start_page: Start page
312 field_subproject: Subproject
313 field_subproject: Subproject
313 field_hours: Hours
314 field_hours: Hours
314 field_activity: Activity
315 field_activity: Activity
315 field_spent_on: Date
316 field_spent_on: Date
316 field_identifier: Identifier
317 field_identifier: Identifier
317 field_is_filter: Used as a filter
318 field_is_filter: Used as a filter
318 field_issue_to: Related issue
319 field_issue_to: Related issue
319 field_delay: Delay
320 field_delay: Delay
320 field_assignable: Issues can be assigned to this role
321 field_assignable: Issues can be assigned to this role
321 field_redirect_existing_links: Redirect existing links
322 field_redirect_existing_links: Redirect existing links
322 field_estimated_hours: Estimated time
323 field_estimated_hours: Estimated time
323 field_column_names: Columns
324 field_column_names: Columns
324 field_time_entries: Log time
325 field_time_entries: Log time
325 field_time_zone: Time zone
326 field_time_zone: Time zone
326 field_searchable: Searchable
327 field_searchable: Searchable
327 field_default_value: Default value
328 field_default_value: Default value
328 field_comments_sorting: Display comments
329 field_comments_sorting: Display comments
329 field_parent_title: Parent page
330 field_parent_title: Parent page
330 field_editable: Editable
331 field_editable: Editable
331 field_watcher: Watcher
332 field_watcher: Watcher
332 field_identity_url: OpenID URL
333 field_identity_url: OpenID URL
333 field_content: Content
334 field_content: Content
334 field_group_by: Group results by
335 field_group_by: Group results by
335 field_sharing: Sharing
336 field_sharing: Sharing
336 field_parent_issue: Parent task
337 field_parent_issue: Parent task
337 field_member_of_group: "Assignee's group"
338 field_member_of_group: "Assignee's group"
338 field_assigned_to_role: "Assignee's role"
339 field_assigned_to_role: "Assignee's role"
339 field_text: Text field
340 field_text: Text field
340 field_visible: Visible
341 field_visible: Visible
341 field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text"
342 field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text"
342 field_issues_visibility: Issues visibility
343 field_issues_visibility: Issues visibility
343 field_is_private: Private
344 field_is_private: Private
344 field_commit_logs_encoding: Commit messages encoding
345 field_commit_logs_encoding: Commit messages encoding
345 field_scm_path_encoding: Path encoding
346 field_scm_path_encoding: Path encoding
346 field_path_to_repository: Path to repository
347 field_path_to_repository: Path to repository
347 field_root_directory: Root directory
348 field_root_directory: Root directory
348 field_cvsroot: CVSROOT
349 field_cvsroot: CVSROOT
349 field_cvs_module: Module
350 field_cvs_module: Module
350 field_repository_is_default: Main repository
351 field_repository_is_default: Main repository
351 field_multiple: Multiple values
352 field_multiple: Multiple values
352 field_auth_source_ldap_filter: LDAP filter
353 field_auth_source_ldap_filter: LDAP filter
353 field_core_fields: Standard fields
354 field_core_fields: Standard fields
354 field_timeout: "Timeout (in seconds)"
355 field_timeout: "Timeout (in seconds)"
355 field_board_parent: Parent forum
356 field_board_parent: Parent forum
356 field_private_notes: Private notes
357 field_private_notes: Private notes
357 field_inherit_members: Inherit members
358 field_inherit_members: Inherit members
358 field_generate_password: Generate password
359 field_generate_password: Generate password
359 field_must_change_passwd: Must change password at next logon
360 field_must_change_passwd: Must change password at next logon
360 field_default_status: Default status
361 field_default_status: Default status
361 field_users_visibility: Users visibility
362 field_users_visibility: Users visibility
362 field_time_entries_visibility: Time logs visibility
363 field_time_entries_visibility: Time logs visibility
363 field_total_estimated_hours: Total estimated time
364 field_total_estimated_hours: Total estimated time
364 field_default_version: Default version
365 field_default_version: Default version
365 field_remote_ip: IP address
366 field_remote_ip: IP address
366
367
367 setting_app_title: Application title
368 setting_app_title: Application title
368 setting_app_subtitle: Application subtitle
369 setting_app_subtitle: Application subtitle
369 setting_welcome_text: Welcome text
370 setting_welcome_text: Welcome text
370 setting_default_language: Default language
371 setting_default_language: Default language
371 setting_login_required: Authentication required
372 setting_login_required: Authentication required
372 setting_self_registration: Self-registration
373 setting_self_registration: Self-registration
373 setting_attachment_max_size: Maximum attachment size
374 setting_attachment_max_size: Maximum attachment size
374 setting_issues_export_limit: Issues export limit
375 setting_issues_export_limit: Issues export limit
375 setting_mail_from: Emission email address
376 setting_mail_from: Emission email address
376 setting_bcc_recipients: Blind carbon copy recipients (bcc)
377 setting_bcc_recipients: Blind carbon copy recipients (bcc)
377 setting_plain_text_mail: Plain text mail (no HTML)
378 setting_plain_text_mail: Plain text mail (no HTML)
378 setting_host_name: Host name and path
379 setting_host_name: Host name and path
379 setting_text_formatting: Text formatting
380 setting_text_formatting: Text formatting
380 setting_wiki_compression: Wiki history compression
381 setting_wiki_compression: Wiki history compression
381 setting_feeds_limit: Maximum number of items in Atom feeds
382 setting_feeds_limit: Maximum number of items in Atom feeds
382 setting_default_projects_public: New projects are public by default
383 setting_default_projects_public: New projects are public by default
383 setting_autofetch_changesets: Fetch commits automatically
384 setting_autofetch_changesets: Fetch commits automatically
384 setting_sys_api_enabled: Enable WS for repository management
385 setting_sys_api_enabled: Enable WS for repository management
385 setting_commit_ref_keywords: Referencing keywords
386 setting_commit_ref_keywords: Referencing keywords
386 setting_commit_fix_keywords: Fixing keywords
387 setting_commit_fix_keywords: Fixing keywords
387 setting_autologin: Autologin
388 setting_autologin: Autologin
388 setting_date_format: Date format
389 setting_date_format: Date format
389 setting_time_format: Time format
390 setting_time_format: Time format
390 setting_cross_project_issue_relations: Allow cross-project issue relations
391 setting_cross_project_issue_relations: Allow cross-project issue relations
391 setting_cross_project_subtasks: Allow cross-project subtasks
392 setting_cross_project_subtasks: Allow cross-project subtasks
392 setting_issue_list_default_columns: Default columns displayed on the issue list
393 setting_issue_list_default_columns: Default columns displayed on the issue list
393 setting_repositories_encodings: Attachments and repositories encodings
394 setting_repositories_encodings: Attachments and repositories encodings
394 setting_emails_header: Email header
395 setting_emails_header: Email header
395 setting_emails_footer: Email footer
396 setting_emails_footer: Email footer
396 setting_protocol: Protocol
397 setting_protocol: Protocol
397 setting_per_page_options: Objects per page options
398 setting_per_page_options: Objects per page options
398 setting_user_format: Users display format
399 setting_user_format: Users display format
399 setting_activity_days_default: Days displayed on project activity
400 setting_activity_days_default: Days displayed on project activity
400 setting_display_subprojects_issues: Display subprojects issues on main projects by default
401 setting_display_subprojects_issues: Display subprojects issues on main projects by default
401 setting_enabled_scm: Enabled SCM
402 setting_enabled_scm: Enabled SCM
402 setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
403 setting_mail_handler_body_delimiters: "Truncate emails after one of these lines"
403 setting_mail_handler_api_enabled: Enable WS for incoming emails
404 setting_mail_handler_api_enabled: Enable WS for incoming emails
404 setting_mail_handler_api_key: Incoming email WS API key
405 setting_mail_handler_api_key: Incoming email WS API key
405 setting_sys_api_key: Repository management WS API key
406 setting_sys_api_key: Repository management WS API key
406 setting_sequential_project_identifiers: Generate sequential project identifiers
407 setting_sequential_project_identifiers: Generate sequential project identifiers
407 setting_gravatar_enabled: Use Gravatar user icons
408 setting_gravatar_enabled: Use Gravatar user icons
408 setting_gravatar_default: Default Gravatar image
409 setting_gravatar_default: Default Gravatar image
409 setting_diff_max_lines_displayed: Maximum number of diff lines displayed
410 setting_diff_max_lines_displayed: Maximum number of diff lines displayed
410 setting_file_max_size_displayed: Maximum size of text files displayed inline
411 setting_file_max_size_displayed: Maximum size of text files displayed inline
411 setting_repository_log_display_limit: Maximum number of revisions displayed on file log
412 setting_repository_log_display_limit: Maximum number of revisions displayed on file log
412 setting_openid: Allow OpenID login and registration
413 setting_openid: Allow OpenID login and registration
413 setting_password_max_age: Require password change after
414 setting_password_max_age: Require password change after
414 setting_password_min_length: Minimum password length
415 setting_password_min_length: Minimum password length
415 setting_lost_password: Allow password reset via email
416 setting_lost_password: Allow password reset via email
416 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
417 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
417 setting_default_projects_modules: Default enabled modules for new projects
418 setting_default_projects_modules: Default enabled modules for new projects
418 setting_issue_done_ratio: Calculate the issue done ratio with
419 setting_issue_done_ratio: Calculate the issue done ratio with
419 setting_issue_done_ratio_issue_field: Use the issue field
420 setting_issue_done_ratio_issue_field: Use the issue field
420 setting_issue_done_ratio_issue_status: Use the issue status
421 setting_issue_done_ratio_issue_status: Use the issue status
421 setting_start_of_week: Start calendars on
422 setting_start_of_week: Start calendars on
422 setting_rest_api_enabled: Enable REST web service
423 setting_rest_api_enabled: Enable REST web service
423 setting_cache_formatted_text: Cache formatted text
424 setting_cache_formatted_text: Cache formatted text
424 setting_default_notification_option: Default notification option
425 setting_default_notification_option: Default notification option
425 setting_commit_logtime_enabled: Enable time logging
426 setting_commit_logtime_enabled: Enable time logging
426 setting_commit_logtime_activity_id: Activity for logged time
427 setting_commit_logtime_activity_id: Activity for logged time
427 setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
428 setting_gantt_items_limit: Maximum number of items displayed on the gantt chart
428 setting_issue_group_assignment: Allow issue assignment to groups
429 setting_issue_group_assignment: Allow issue assignment to groups
429 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
430 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
430 setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed
431 setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed
431 setting_unsubscribe: Allow users to delete their own account
432 setting_unsubscribe: Allow users to delete their own account
432 setting_session_lifetime: Session maximum lifetime
433 setting_session_lifetime: Session maximum lifetime
433 setting_session_timeout: Session inactivity timeout
434 setting_session_timeout: Session inactivity timeout
434 setting_thumbnails_enabled: Display attachment thumbnails
435 setting_thumbnails_enabled: Display attachment thumbnails
435 setting_thumbnails_size: Thumbnails size (in pixels)
436 setting_thumbnails_size: Thumbnails size (in pixels)
436 setting_non_working_week_days: Non-working days
437 setting_non_working_week_days: Non-working days
437 setting_jsonp_enabled: Enable JSONP support
438 setting_jsonp_enabled: Enable JSONP support
438 setting_default_projects_tracker_ids: Default trackers for new projects
439 setting_default_projects_tracker_ids: Default trackers for new projects
439 setting_mail_handler_excluded_filenames: Exclude attachments by name
440 setting_mail_handler_excluded_filenames: Exclude attachments by name
440 setting_force_default_language_for_anonymous: Force default language for anonymous users
441 setting_force_default_language_for_anonymous: Force default language for anonymous users
441 setting_force_default_language_for_loggedin: Force default language for logged-in users
442 setting_force_default_language_for_loggedin: Force default language for logged-in users
442 setting_link_copied_issue: Link issues on copy
443 setting_link_copied_issue: Link issues on copy
443 setting_max_additional_emails: Maximum number of additional email addresses
444 setting_max_additional_emails: Maximum number of additional email addresses
444 setting_search_results_per_page: Search results per page
445 setting_search_results_per_page: Search results per page
445 setting_attachment_extensions_allowed: Allowed extensions
446 setting_attachment_extensions_allowed: Allowed extensions
446 setting_attachment_extensions_denied: Disallowed extensions
447 setting_attachment_extensions_denied: Disallowed extensions
447 setting_new_item_menu_tab: Project menu tab for creating new objects
448 setting_new_item_menu_tab: Project menu tab for creating new objects
448
449
449 permission_add_project: Create project
450 permission_add_project: Create project
450 permission_add_subprojects: Create subprojects
451 permission_add_subprojects: Create subprojects
451 permission_edit_project: Edit project
452 permission_edit_project: Edit project
452 permission_close_project: Close / reopen the project
453 permission_close_project: Close / reopen the project
453 permission_select_project_modules: Select project modules
454 permission_select_project_modules: Select project modules
454 permission_manage_members: Manage members
455 permission_manage_members: Manage members
455 permission_manage_project_activities: Manage project activities
456 permission_manage_project_activities: Manage project activities
456 permission_manage_versions: Manage versions
457 permission_manage_versions: Manage versions
457 permission_manage_categories: Manage issue categories
458 permission_manage_categories: Manage issue categories
458 permission_view_issues: View Issues
459 permission_view_issues: View Issues
459 permission_add_issues: Add issues
460 permission_add_issues: Add issues
460 permission_edit_issues: Edit issues
461 permission_edit_issues: Edit issues
461 permission_copy_issues: Copy issues
462 permission_copy_issues: Copy issues
462 permission_manage_issue_relations: Manage issue relations
463 permission_manage_issue_relations: Manage issue relations
463 permission_set_issues_private: Set issues public or private
464 permission_set_issues_private: Set issues public or private
464 permission_set_own_issues_private: Set own issues public or private
465 permission_set_own_issues_private: Set own issues public or private
465 permission_add_issue_notes: Add notes
466 permission_add_issue_notes: Add notes
466 permission_edit_issue_notes: Edit notes
467 permission_edit_issue_notes: Edit notes
467 permission_edit_own_issue_notes: Edit own notes
468 permission_edit_own_issue_notes: Edit own notes
468 permission_view_private_notes: View private notes
469 permission_view_private_notes: View private notes
469 permission_set_notes_private: Set notes as private
470 permission_set_notes_private: Set notes as private
470 permission_move_issues: Move issues
471 permission_move_issues: Move issues
471 permission_delete_issues: Delete issues
472 permission_delete_issues: Delete issues
472 permission_manage_public_queries: Manage public queries
473 permission_manage_public_queries: Manage public queries
473 permission_save_queries: Save queries
474 permission_save_queries: Save queries
474 permission_view_gantt: View gantt chart
475 permission_view_gantt: View gantt chart
475 permission_view_calendar: View calendar
476 permission_view_calendar: View calendar
476 permission_view_issue_watchers: View watchers list
477 permission_view_issue_watchers: View watchers list
477 permission_add_issue_watchers: Add watchers
478 permission_add_issue_watchers: Add watchers
478 permission_delete_issue_watchers: Delete watchers
479 permission_delete_issue_watchers: Delete watchers
479 permission_log_time: Log spent time
480 permission_log_time: Log spent time
480 permission_view_time_entries: View spent time
481 permission_view_time_entries: View spent time
481 permission_edit_time_entries: Edit time logs
482 permission_edit_time_entries: Edit time logs
482 permission_edit_own_time_entries: Edit own time logs
483 permission_edit_own_time_entries: Edit own time logs
483 permission_manage_news: Manage news
484 permission_manage_news: Manage news
484 permission_comment_news: Comment news
485 permission_comment_news: Comment news
485 permission_view_documents: View documents
486 permission_view_documents: View documents
486 permission_add_documents: Add documents
487 permission_add_documents: Add documents
487 permission_edit_documents: Edit documents
488 permission_edit_documents: Edit documents
488 permission_delete_documents: Delete documents
489 permission_delete_documents: Delete documents
489 permission_manage_files: Manage files
490 permission_manage_files: Manage files
490 permission_view_files: View files
491 permission_view_files: View files
491 permission_manage_wiki: Manage wiki
492 permission_manage_wiki: Manage wiki
492 permission_rename_wiki_pages: Rename wiki pages
493 permission_rename_wiki_pages: Rename wiki pages
493 permission_delete_wiki_pages: Delete wiki pages
494 permission_delete_wiki_pages: Delete wiki pages
494 permission_view_wiki_pages: View wiki
495 permission_view_wiki_pages: View wiki
495 permission_view_wiki_edits: View wiki history
496 permission_view_wiki_edits: View wiki history
496 permission_edit_wiki_pages: Edit wiki pages
497 permission_edit_wiki_pages: Edit wiki pages
497 permission_delete_wiki_pages_attachments: Delete attachments
498 permission_delete_wiki_pages_attachments: Delete attachments
498 permission_protect_wiki_pages: Protect wiki pages
499 permission_protect_wiki_pages: Protect wiki pages
499 permission_manage_repository: Manage repository
500 permission_manage_repository: Manage repository
500 permission_browse_repository: Browse repository
501 permission_browse_repository: Browse repository
501 permission_view_changesets: View changesets
502 permission_view_changesets: View changesets
502 permission_commit_access: Commit access
503 permission_commit_access: Commit access
503 permission_manage_boards: Manage forums
504 permission_manage_boards: Manage forums
504 permission_view_messages: View messages
505 permission_view_messages: View messages
505 permission_add_messages: Post messages
506 permission_add_messages: Post messages
506 permission_edit_messages: Edit messages
507 permission_edit_messages: Edit messages
507 permission_edit_own_messages: Edit own messages
508 permission_edit_own_messages: Edit own messages
508 permission_delete_messages: Delete messages
509 permission_delete_messages: Delete messages
509 permission_delete_own_messages: Delete own messages
510 permission_delete_own_messages: Delete own messages
510 permission_export_wiki_pages: Export wiki pages
511 permission_export_wiki_pages: Export wiki pages
511 permission_manage_subtasks: Manage subtasks
512 permission_manage_subtasks: Manage subtasks
512 permission_manage_related_issues: Manage related issues
513 permission_manage_related_issues: Manage related issues
513 permission_import_issues: Import issues
514 permission_import_issues: Import issues
514
515
515 project_module_issue_tracking: Issue tracking
516 project_module_issue_tracking: Issue tracking
516 project_module_time_tracking: Time tracking
517 project_module_time_tracking: Time tracking
517 project_module_news: News
518 project_module_news: News
518 project_module_documents: Documents
519 project_module_documents: Documents
519 project_module_files: Files
520 project_module_files: Files
520 project_module_wiki: Wiki
521 project_module_wiki: Wiki
521 project_module_repository: Repository
522 project_module_repository: Repository
522 project_module_boards: Forums
523 project_module_boards: Forums
523 project_module_calendar: Calendar
524 project_module_calendar: Calendar
524 project_module_gantt: Gantt
525 project_module_gantt: Gantt
525
526
526 label_user: User
527 label_user: User
527 label_user_plural: Users
528 label_user_plural: Users
528 label_user_new: New user
529 label_user_new: New user
529 label_user_anonymous: Anonymous
530 label_user_anonymous: Anonymous
530 label_project: Project
531 label_project: Project
531 label_project_new: New project
532 label_project_new: New project
532 label_project_plural: Projects
533 label_project_plural: Projects
533 label_x_projects:
534 label_x_projects:
534 zero: no projects
535 zero: no projects
535 one: 1 project
536 one: 1 project
536 other: "%{count} projects"
537 other: "%{count} projects"
537 label_project_all: All Projects
538 label_project_all: All Projects
538 label_project_latest: Latest projects
539 label_project_latest: Latest projects
539 label_issue: Issue
540 label_issue: Issue
540 label_issue_new: New issue
541 label_issue_new: New issue
541 label_issue_plural: Issues
542 label_issue_plural: Issues
542 label_issue_view_all: View all issues
543 label_issue_view_all: View all issues
543 label_issues_by: "Issues by %{value}"
544 label_issues_by: "Issues by %{value}"
544 label_issue_added: Issue added
545 label_issue_added: Issue added
545 label_issue_updated: Issue updated
546 label_issue_updated: Issue updated
546 label_issue_note_added: Note added
547 label_issue_note_added: Note added
547 label_issue_status_updated: Status updated
548 label_issue_status_updated: Status updated
548 label_issue_assigned_to_updated: Assignee updated
549 label_issue_assigned_to_updated: Assignee updated
549 label_issue_priority_updated: Priority updated
550 label_issue_priority_updated: Priority updated
550 label_document: Document
551 label_document: Document
551 label_document_new: New document
552 label_document_new: New document
552 label_document_plural: Documents
553 label_document_plural: Documents
553 label_document_added: Document added
554 label_document_added: Document added
554 label_role: Role
555 label_role: Role
555 label_role_plural: Roles
556 label_role_plural: Roles
556 label_role_new: New role
557 label_role_new: New role
557 label_role_and_permissions: Roles and permissions
558 label_role_and_permissions: Roles and permissions
558 label_role_anonymous: Anonymous
559 label_role_anonymous: Anonymous
559 label_role_non_member: Non member
560 label_role_non_member: Non member
560 label_member: Member
561 label_member: Member
561 label_member_new: New member
562 label_member_new: New member
562 label_member_plural: Members
563 label_member_plural: Members
563 label_tracker: Tracker
564 label_tracker: Tracker
564 label_tracker_plural: Trackers
565 label_tracker_plural: Trackers
565 label_tracker_all: All trackers
566 label_tracker_all: All trackers
566 label_tracker_new: New tracker
567 label_tracker_new: New tracker
567 label_workflow: Workflow
568 label_workflow: Workflow
568 label_issue_status: Issue status
569 label_issue_status: Issue status
569 label_issue_status_plural: Issue statuses
570 label_issue_status_plural: Issue statuses
570 label_issue_status_new: New status
571 label_issue_status_new: New status
571 label_issue_category: Issue category
572 label_issue_category: Issue category
572 label_issue_category_plural: Issue categories
573 label_issue_category_plural: Issue categories
573 label_issue_category_new: New category
574 label_issue_category_new: New category
574 label_custom_field: Custom field
575 label_custom_field: Custom field
575 label_custom_field_plural: Custom fields
576 label_custom_field_plural: Custom fields
576 label_custom_field_new: New custom field
577 label_custom_field_new: New custom field
577 label_enumerations: Enumerations
578 label_enumerations: Enumerations
578 label_enumeration_new: New value
579 label_enumeration_new: New value
579 label_information: Information
580 label_information: Information
580 label_information_plural: Information
581 label_information_plural: Information
581 label_please_login: Please log in
582 label_please_login: Please log in
582 label_register: Register
583 label_register: Register
583 label_login_with_open_id_option: or login with OpenID
584 label_login_with_open_id_option: or login with OpenID
584 label_password_lost: Lost password
585 label_password_lost: Lost password
585 label_password_required: Confirm your password to continue
586 label_password_required: Confirm your password to continue
586 label_home: Home
587 label_home: Home
587 label_my_page: My page
588 label_my_page: My page
588 label_my_account: My account
589 label_my_account: My account
589 label_my_projects: My projects
590 label_my_projects: My projects
590 label_my_page_block: My page block
591 label_my_page_block: My page block
591 label_administration: Administration
592 label_administration: Administration
592 label_login: Sign in
593 label_login: Sign in
593 label_logout: Sign out
594 label_logout: Sign out
594 label_help: Help
595 label_help: Help
595 label_reported_issues: Reported issues
596 label_reported_issues: Reported issues
596 label_assigned_issues: Assigned issues
597 label_assigned_issues: Assigned issues
597 label_assigned_to_me_issues: Issues assigned to me
598 label_assigned_to_me_issues: Issues assigned to me
598 label_last_login: Last connection
599 label_last_login: Last connection
599 label_registered_on: Registered on
600 label_registered_on: Registered on
600 label_activity: Activity
601 label_activity: Activity
601 label_overall_activity: Overall activity
602 label_overall_activity: Overall activity
602 label_user_activity: "%{value}'s activity"
603 label_user_activity: "%{value}'s activity"
603 label_new: New
604 label_new: New
604 label_logged_as: Logged in as
605 label_logged_as: Logged in as
605 label_environment: Environment
606 label_environment: Environment
606 label_authentication: Authentication
607 label_authentication: Authentication
607 label_auth_source: Authentication mode
608 label_auth_source: Authentication mode
608 label_auth_source_new: New authentication mode
609 label_auth_source_new: New authentication mode
609 label_auth_source_plural: Authentication modes
610 label_auth_source_plural: Authentication modes
610 label_subproject_plural: Subprojects
611 label_subproject_plural: Subprojects
611 label_subproject_new: New subproject
612 label_subproject_new: New subproject
612 label_and_its_subprojects: "%{value} and its subprojects"
613 label_and_its_subprojects: "%{value} and its subprojects"
613 label_min_max_length: Min - Max length
614 label_min_max_length: Min - Max length
614 label_list: List
615 label_list: List
615 label_date: Date
616 label_date: Date
616 label_integer: Integer
617 label_integer: Integer
617 label_float: Float
618 label_float: Float
618 label_boolean: Boolean
619 label_boolean: Boolean
619 label_string: Text
620 label_string: Text
620 label_text: Long text
621 label_text: Long text
621 label_attribute: Attribute
622 label_attribute: Attribute
622 label_attribute_plural: Attributes
623 label_attribute_plural: Attributes
623 label_no_data: No data to display
624 label_no_data: No data to display
624 label_no_preview: No preview available
625 label_no_preview: No preview available
625 label_change_status: Change status
626 label_change_status: Change status
626 label_history: History
627 label_history: History
627 label_attachment: File
628 label_attachment: File
628 label_attachment_new: New file
629 label_attachment_new: New file
629 label_attachment_delete: Delete file
630 label_attachment_delete: Delete file
630 label_attachment_plural: Files
631 label_attachment_plural: Files
631 label_file_added: File added
632 label_file_added: File added
632 label_report: Report
633 label_report: Report
633 label_report_plural: Reports
634 label_report_plural: Reports
634 label_news: News
635 label_news: News
635 label_news_new: Add news
636 label_news_new: Add news
636 label_news_plural: News
637 label_news_plural: News
637 label_news_latest: Latest news
638 label_news_latest: Latest news
638 label_news_view_all: View all news
639 label_news_view_all: View all news
639 label_news_added: News added
640 label_news_added: News added
640 label_news_comment_added: Comment added to a news
641 label_news_comment_added: Comment added to a news
641 label_settings: Settings
642 label_settings: Settings
642 label_overview: Overview
643 label_overview: Overview
643 label_version: Version
644 label_version: Version
644 label_version_new: New version
645 label_version_new: New version
645 label_version_plural: Versions
646 label_version_plural: Versions
646 label_close_versions: Close completed versions
647 label_close_versions: Close completed versions
647 label_confirmation: Confirmation
648 label_confirmation: Confirmation
648 label_export_to: 'Also available in:'
649 label_export_to: 'Also available in:'
649 label_read: Read...
650 label_read: Read...
650 label_public_projects: Public projects
651 label_public_projects: Public projects
651 label_open_issues: open
652 label_open_issues: open
652 label_open_issues_plural: open
653 label_open_issues_plural: open
653 label_closed_issues: closed
654 label_closed_issues: closed
654 label_closed_issues_plural: closed
655 label_closed_issues_plural: closed
655 label_x_open_issues_abbr:
656 label_x_open_issues_abbr:
656 zero: 0 open
657 zero: 0 open
657 one: 1 open
658 one: 1 open
658 other: "%{count} open"
659 other: "%{count} open"
659 label_x_closed_issues_abbr:
660 label_x_closed_issues_abbr:
660 zero: 0 closed
661 zero: 0 closed
661 one: 1 closed
662 one: 1 closed
662 other: "%{count} closed"
663 other: "%{count} closed"
663 label_x_issues:
664 label_x_issues:
664 zero: 0 issues
665 zero: 0 issues
665 one: 1 issue
666 one: 1 issue
666 other: "%{count} issues"
667 other: "%{count} issues"
667 label_total: Total
668 label_total: Total
668 label_total_plural: Totals
669 label_total_plural: Totals
669 label_total_time: Total time
670 label_total_time: Total time
670 label_permissions: Permissions
671 label_permissions: Permissions
671 label_current_status: Current status
672 label_current_status: Current status
672 label_new_statuses_allowed: New statuses allowed
673 label_new_statuses_allowed: New statuses allowed
673 label_all: all
674 label_all: all
674 label_any: any
675 label_any: any
675 label_none: none
676 label_none: none
676 label_nobody: nobody
677 label_nobody: nobody
677 label_next: Next
678 label_next: Next
678 label_previous: Previous
679 label_previous: Previous
679 label_used_by: Used by
680 label_used_by: Used by
680 label_details: Details
681 label_details: Details
681 label_add_note: Add a note
682 label_add_note: Add a note
682 label_calendar: Calendar
683 label_calendar: Calendar
683 label_months_from: months from
684 label_months_from: months from
684 label_gantt: Gantt
685 label_gantt: Gantt
685 label_internal: Internal
686 label_internal: Internal
686 label_last_changes: "last %{count} changes"
687 label_last_changes: "last %{count} changes"
687 label_change_view_all: View all changes
688 label_change_view_all: View all changes
688 label_personalize_page: Personalize this page
689 label_personalize_page: Personalize this page
689 label_comment: Comment
690 label_comment: Comment
690 label_comment_plural: Comments
691 label_comment_plural: Comments
691 label_x_comments:
692 label_x_comments:
692 zero: no comments
693 zero: no comments
693 one: 1 comment
694 one: 1 comment
694 other: "%{count} comments"
695 other: "%{count} comments"
695 label_comment_add: Add a comment
696 label_comment_add: Add a comment
696 label_comment_added: Comment added
697 label_comment_added: Comment added
697 label_comment_delete: Delete comments
698 label_comment_delete: Delete comments
698 label_query: Custom query
699 label_query: Custom query
699 label_query_plural: Custom queries
700 label_query_plural: Custom queries
700 label_query_new: New query
701 label_query_new: New query
701 label_my_queries: My custom queries
702 label_my_queries: My custom queries
702 label_filter_add: Add filter
703 label_filter_add: Add filter
703 label_filter_plural: Filters
704 label_filter_plural: Filters
704 label_equals: is
705 label_equals: is
705 label_not_equals: is not
706 label_not_equals: is not
706 label_in_less_than: in less than
707 label_in_less_than: in less than
707 label_in_more_than: in more than
708 label_in_more_than: in more than
708 label_in_the_next_days: in the next
709 label_in_the_next_days: in the next
709 label_in_the_past_days: in the past
710 label_in_the_past_days: in the past
710 label_greater_or_equal: '>='
711 label_greater_or_equal: '>='
711 label_less_or_equal: '<='
712 label_less_or_equal: '<='
712 label_between: between
713 label_between: between
713 label_in: in
714 label_in: in
714 label_today: today
715 label_today: today
715 label_all_time: all time
716 label_all_time: all time
716 label_yesterday: yesterday
717 label_yesterday: yesterday
717 label_this_week: this week
718 label_this_week: this week
718 label_last_week: last week
719 label_last_week: last week
719 label_last_n_weeks: "last %{count} weeks"
720 label_last_n_weeks: "last %{count} weeks"
720 label_last_n_days: "last %{count} days"
721 label_last_n_days: "last %{count} days"
721 label_this_month: this month
722 label_this_month: this month
722 label_last_month: last month
723 label_last_month: last month
723 label_this_year: this year
724 label_this_year: this year
724 label_date_range: Date range
725 label_date_range: Date range
725 label_less_than_ago: less than days ago
726 label_less_than_ago: less than days ago
726 label_more_than_ago: more than days ago
727 label_more_than_ago: more than days ago
727 label_ago: days ago
728 label_ago: days ago
728 label_contains: contains
729 label_contains: contains
729 label_not_contains: doesn't contain
730 label_not_contains: doesn't contain
730 label_any_issues_in_project: any issues in project
731 label_any_issues_in_project: any issues in project
731 label_any_issues_not_in_project: any issues not in project
732 label_any_issues_not_in_project: any issues not in project
732 label_no_issues_in_project: no issues in project
733 label_no_issues_in_project: no issues in project
733 label_any_open_issues: any open issues
734 label_any_open_issues: any open issues
734 label_no_open_issues: no open issues
735 label_no_open_issues: no open issues
735 label_day_plural: days
736 label_day_plural: days
736 label_repository: Repository
737 label_repository: Repository
737 label_repository_new: New repository
738 label_repository_new: New repository
738 label_repository_plural: Repositories
739 label_repository_plural: Repositories
739 label_browse: Browse
740 label_browse: Browse
740 label_branch: Branch
741 label_branch: Branch
741 label_tag: Tag
742 label_tag: Tag
742 label_revision: Revision
743 label_revision: Revision
743 label_revision_plural: Revisions
744 label_revision_plural: Revisions
744 label_revision_id: "Revision %{value}"
745 label_revision_id: "Revision %{value}"
745 label_associated_revisions: Associated revisions
746 label_associated_revisions: Associated revisions
746 label_added: added
747 label_added: added
747 label_modified: modified
748 label_modified: modified
748 label_copied: copied
749 label_copied: copied
749 label_renamed: renamed
750 label_renamed: renamed
750 label_deleted: deleted
751 label_deleted: deleted
751 label_latest_revision: Latest revision
752 label_latest_revision: Latest revision
752 label_latest_revision_plural: Latest revisions
753 label_latest_revision_plural: Latest revisions
753 label_view_revisions: View revisions
754 label_view_revisions: View revisions
754 label_view_all_revisions: View all revisions
755 label_view_all_revisions: View all revisions
755 label_max_size: Maximum size
756 label_max_size: Maximum size
756 label_sort_highest: Move to top
757 label_sort_highest: Move to top
757 label_sort_higher: Move up
758 label_sort_higher: Move up
758 label_sort_lower: Move down
759 label_sort_lower: Move down
759 label_sort_lowest: Move to bottom
760 label_sort_lowest: Move to bottom
760 label_roadmap: Roadmap
761 label_roadmap: Roadmap
761 label_roadmap_due_in: "Due in %{value}"
762 label_roadmap_due_in: "Due in %{value}"
762 label_roadmap_overdue: "%{value} late"
763 label_roadmap_overdue: "%{value} late"
763 label_roadmap_no_issues: No issues for this version
764 label_roadmap_no_issues: No issues for this version
764 label_search: Search
765 label_search: Search
765 label_result_plural: Results
766 label_result_plural: Results
766 label_all_words: All words
767 label_all_words: All words
767 label_wiki: Wiki
768 label_wiki: Wiki
768 label_wiki_edit: Wiki edit
769 label_wiki_edit: Wiki edit
769 label_wiki_edit_plural: Wiki edits
770 label_wiki_edit_plural: Wiki edits
770 label_wiki_page: Wiki page
771 label_wiki_page: Wiki page
771 label_wiki_page_plural: Wiki pages
772 label_wiki_page_plural: Wiki pages
772 label_wiki_page_new: New wiki page
773 label_wiki_page_new: New wiki page
773 label_index_by_title: Index by title
774 label_index_by_title: Index by title
774 label_index_by_date: Index by date
775 label_index_by_date: Index by date
775 label_current_version: Current version
776 label_current_version: Current version
776 label_preview: Preview
777 label_preview: Preview
777 label_feed_plural: Feeds
778 label_feed_plural: Feeds
778 label_changes_details: Details of all changes
779 label_changes_details: Details of all changes
779 label_issue_tracking: Issue tracking
780 label_issue_tracking: Issue tracking
780 label_spent_time: Spent time
781 label_spent_time: Spent time
781 label_total_spent_time: Total spent time
782 label_total_spent_time: Total spent time
782 label_overall_spent_time: Overall spent time
783 label_overall_spent_time: Overall spent time
783 label_f_hour: "%{value} hour"
784 label_f_hour: "%{value} hour"
784 label_f_hour_plural: "%{value} hours"
785 label_f_hour_plural: "%{value} hours"
785 label_f_hour_short: "%{value} h"
786 label_f_hour_short: "%{value} h"
786 label_time_tracking: Time tracking
787 label_time_tracking: Time tracking
787 label_change_plural: Changes
788 label_change_plural: Changes
788 label_statistics: Statistics
789 label_statistics: Statistics
789 label_commits_per_month: Commits per month
790 label_commits_per_month: Commits per month
790 label_commits_per_author: Commits per author
791 label_commits_per_author: Commits per author
791 label_diff: diff
792 label_diff: diff
792 label_view_diff: View differences
793 label_view_diff: View differences
793 label_diff_inline: inline
794 label_diff_inline: inline
794 label_diff_side_by_side: side by side
795 label_diff_side_by_side: side by side
795 label_options: Options
796 label_options: Options
796 label_copy_workflow_from: Copy workflow from
797 label_copy_workflow_from: Copy workflow from
797 label_permissions_report: Permissions report
798 label_permissions_report: Permissions report
798 label_watched_issues: Watched issues
799 label_watched_issues: Watched issues
799 label_related_issues: Related issues
800 label_related_issues: Related issues
800 label_applied_status: Applied status
801 label_applied_status: Applied status
801 label_loading: Loading...
802 label_loading: Loading...
802 label_relation_new: New relation
803 label_relation_new: New relation
803 label_relation_delete: Delete relation
804 label_relation_delete: Delete relation
804 label_relates_to: Related to
805 label_relates_to: Related to
805 label_duplicates: Duplicates
806 label_duplicates: Duplicates
806 label_duplicated_by: Duplicated by
807 label_duplicated_by: Duplicated by
807 label_blocks: Blocks
808 label_blocks: Blocks
808 label_blocked_by: Blocked by
809 label_blocked_by: Blocked by
809 label_precedes: Precedes
810 label_precedes: Precedes
810 label_follows: Follows
811 label_follows: Follows
811 label_copied_to: Copied to
812 label_copied_to: Copied to
812 label_copied_from: Copied from
813 label_copied_from: Copied from
813 label_stay_logged_in: Stay logged in
814 label_stay_logged_in: Stay logged in
814 label_disabled: disabled
815 label_disabled: disabled
815 label_show_completed_versions: Show completed versions
816 label_show_completed_versions: Show completed versions
816 label_me: me
817 label_me: me
817 label_board: Forum
818 label_board: Forum
818 label_board_new: New forum
819 label_board_new: New forum
819 label_board_plural: Forums
820 label_board_plural: Forums
820 label_board_locked: Locked
821 label_board_locked: Locked
821 label_board_sticky: Sticky
822 label_board_sticky: Sticky
822 label_topic_plural: Topics
823 label_topic_plural: Topics
823 label_message_plural: Messages
824 label_message_plural: Messages
824 label_message_last: Last message
825 label_message_last: Last message
825 label_message_new: New message
826 label_message_new: New message
826 label_message_posted: Message added
827 label_message_posted: Message added
827 label_reply_plural: Replies
828 label_reply_plural: Replies
828 label_send_information: Send account information to the user
829 label_send_information: Send account information to the user
829 label_year: Year
830 label_year: Year
830 label_month: Month
831 label_month: Month
831 label_week: Week
832 label_week: Week
832 label_date_from: From
833 label_date_from: From
833 label_date_to: To
834 label_date_to: To
834 label_language_based: Based on user's language
835 label_language_based: Based on user's language
835 label_sort_by: "Sort by %{value}"
836 label_sort_by: "Sort by %{value}"
836 label_send_test_email: Send a test email
837 label_send_test_email: Send a test email
837 label_feeds_access_key: Atom access key
838 label_feeds_access_key: Atom access key
838 label_missing_feeds_access_key: Missing a Atom access key
839 label_missing_feeds_access_key: Missing a Atom access key
839 label_feeds_access_key_created_on: "Atom access key created %{value} ago"
840 label_feeds_access_key_created_on: "Atom access key created %{value} ago"
840 label_module_plural: Modules
841 label_module_plural: Modules
841 label_added_time_by: "Added by %{author} %{age} ago"
842 label_added_time_by: "Added by %{author} %{age} ago"
842 label_updated_time_by: "Updated by %{author} %{age} ago"
843 label_updated_time_by: "Updated by %{author} %{age} ago"
843 label_updated_time: "Updated %{value} ago"
844 label_updated_time: "Updated %{value} ago"
844 label_jump_to_a_project: Jump to a project...
845 label_jump_to_a_project: Jump to a project...
845 label_file_plural: Files
846 label_file_plural: Files
846 label_changeset_plural: Changesets
847 label_changeset_plural: Changesets
847 label_default_columns: Default columns
848 label_default_columns: Default columns
848 label_no_change_option: (No change)
849 label_no_change_option: (No change)
849 label_bulk_edit_selected_issues: Bulk edit selected issues
850 label_bulk_edit_selected_issues: Bulk edit selected issues
850 label_bulk_edit_selected_time_entries: Bulk edit selected time entries
851 label_bulk_edit_selected_time_entries: Bulk edit selected time entries
851 label_theme: Theme
852 label_theme: Theme
852 label_default: Default
853 label_default: Default
853 label_search_titles_only: Search titles only
854 label_search_titles_only: Search titles only
854 label_user_mail_option_all: "For any event on all my projects"
855 label_user_mail_option_all: "For any event on all my projects"
855 label_user_mail_option_selected: "For any event on the selected projects only..."
856 label_user_mail_option_selected: "For any event on the selected projects only..."
856 label_user_mail_option_none: "No events"
857 label_user_mail_option_none: "No events"
857 label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in"
858 label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in"
858 label_user_mail_option_only_assigned: "Only for things I am assigned to"
859 label_user_mail_option_only_assigned: "Only for things I am assigned to"
859 label_user_mail_option_only_owner: "Only for things I am the owner of"
860 label_user_mail_option_only_owner: "Only for things I am the owner of"
860 label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself"
861 label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself"
861 label_registration_activation_by_email: account activation by email
862 label_registration_activation_by_email: account activation by email
862 label_registration_manual_activation: manual account activation
863 label_registration_manual_activation: manual account activation
863 label_registration_automatic_activation: automatic account activation
864 label_registration_automatic_activation: automatic account activation
864 label_display_per_page: "Per page: %{value}"
865 label_display_per_page: "Per page: %{value}"
865 label_age: Age
866 label_age: Age
866 label_change_properties: Change properties
867 label_change_properties: Change properties
867 label_general: General
868 label_general: General
868 label_more: More
869 label_more: More
869 label_scm: SCM
870 label_scm: SCM
870 label_plugins: Plugins
871 label_plugins: Plugins
871 label_ldap_authentication: LDAP authentication
872 label_ldap_authentication: LDAP authentication
872 label_downloads_abbr: D/L
873 label_downloads_abbr: D/L
873 label_optional_description: Optional description
874 label_optional_description: Optional description
874 label_add_another_file: Add another file
875 label_add_another_file: Add another file
875 label_preferences: Preferences
876 label_preferences: Preferences
876 label_chronological_order: In chronological order
877 label_chronological_order: In chronological order
877 label_reverse_chronological_order: In reverse chronological order
878 label_reverse_chronological_order: In reverse chronological order
878 label_planning: Planning
879 label_planning: Planning
879 label_incoming_emails: Incoming emails
880 label_incoming_emails: Incoming emails
880 label_generate_key: Generate a key
881 label_generate_key: Generate a key
881 label_issue_watchers: Watchers
882 label_issue_watchers: Watchers
882 label_example: Example
883 label_example: Example
883 label_display: Display
884 label_display: Display
884 label_sort: Sort
885 label_sort: Sort
885 label_ascending: Ascending
886 label_ascending: Ascending
886 label_descending: Descending
887 label_descending: Descending
887 label_date_from_to: From %{start} to %{end}
888 label_date_from_to: From %{start} to %{end}
888 label_wiki_content_added: Wiki page added
889 label_wiki_content_added: Wiki page added
889 label_wiki_content_updated: Wiki page updated
890 label_wiki_content_updated: Wiki page updated
890 label_group: Group
891 label_group: Group
891 label_group_plural: Groups
892 label_group_plural: Groups
892 label_group_new: New group
893 label_group_new: New group
893 label_group_anonymous: Anonymous users
894 label_group_anonymous: Anonymous users
894 label_group_non_member: Non member users
895 label_group_non_member: Non member users
895 label_time_entry_plural: Spent time
896 label_time_entry_plural: Spent time
896 label_version_sharing_none: Not shared
897 label_version_sharing_none: Not shared
897 label_version_sharing_descendants: With subprojects
898 label_version_sharing_descendants: With subprojects
898 label_version_sharing_hierarchy: With project hierarchy
899 label_version_sharing_hierarchy: With project hierarchy
899 label_version_sharing_tree: With project tree
900 label_version_sharing_tree: With project tree
900 label_version_sharing_system: With all projects
901 label_version_sharing_system: With all projects
901 label_update_issue_done_ratios: Update issue done ratios
902 label_update_issue_done_ratios: Update issue done ratios
902 label_copy_source: Source
903 label_copy_source: Source
903 label_copy_target: Target
904 label_copy_target: Target
904 label_copy_same_as_target: Same as target
905 label_copy_same_as_target: Same as target
905 label_display_used_statuses_only: Only display statuses that are used by this tracker
906 label_display_used_statuses_only: Only display statuses that are used by this tracker
906 label_api_access_key: API access key
907 label_api_access_key: API access key
907 label_missing_api_access_key: Missing an API access key
908 label_missing_api_access_key: Missing an API access key
908 label_api_access_key_created_on: "API access key created %{value} ago"
909 label_api_access_key_created_on: "API access key created %{value} ago"
909 label_profile: Profile
910 label_profile: Profile
910 label_subtask_plural: Subtasks
911 label_subtask_plural: Subtasks
911 label_project_copy_notifications: Send email notifications during the project copy
912 label_project_copy_notifications: Send email notifications during the project copy
912 label_principal_search: "Search for user or group:"
913 label_principal_search: "Search for user or group:"
913 label_user_search: "Search for user:"
914 label_user_search: "Search for user:"
914 label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author
915 label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author
915 label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee
916 label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee
916 label_issues_visibility_all: All issues
917 label_issues_visibility_all: All issues
917 label_issues_visibility_public: All non private issues
918 label_issues_visibility_public: All non private issues
918 label_issues_visibility_own: Issues created by or assigned to the user
919 label_issues_visibility_own: Issues created by or assigned to the user
919 label_git_report_last_commit: Report last commit for files and directories
920 label_git_report_last_commit: Report last commit for files and directories
920 label_parent_revision: Parent
921 label_parent_revision: Parent
921 label_child_revision: Child
922 label_child_revision: Child
922 label_export_options: "%{export_format} export options"
923 label_export_options: "%{export_format} export options"
923 label_copy_attachments: Copy attachments
924 label_copy_attachments: Copy attachments
924 label_copy_subtasks: Copy subtasks
925 label_copy_subtasks: Copy subtasks
925 label_item_position: "%{position} of %{count}"
926 label_item_position: "%{position} of %{count}"
926 label_completed_versions: Completed versions
927 label_completed_versions: Completed versions
927 label_search_for_watchers: Search for watchers to add
928 label_search_for_watchers: Search for watchers to add
928 label_session_expiration: Session expiration
929 label_session_expiration: Session expiration
929 label_show_closed_projects: View closed projects
930 label_show_closed_projects: View closed projects
930 label_status_transitions: Status transitions
931 label_status_transitions: Status transitions
931 label_fields_permissions: Fields permissions
932 label_fields_permissions: Fields permissions
932 label_readonly: Read-only
933 label_readonly: Read-only
933 label_required: Required
934 label_required: Required
934 label_hidden: Hidden
935 label_hidden: Hidden
935 label_attribute_of_project: "Project's %{name}"
936 label_attribute_of_project: "Project's %{name}"
936 label_attribute_of_issue: "Issue's %{name}"
937 label_attribute_of_issue: "Issue's %{name}"
937 label_attribute_of_author: "Author's %{name}"
938 label_attribute_of_author: "Author's %{name}"
938 label_attribute_of_assigned_to: "Assignee's %{name}"
939 label_attribute_of_assigned_to: "Assignee's %{name}"
939 label_attribute_of_user: "User's %{name}"
940 label_attribute_of_user: "User's %{name}"
940 label_attribute_of_fixed_version: "Target version's %{name}"
941 label_attribute_of_fixed_version: "Target version's %{name}"
941 label_cross_project_descendants: With subprojects
942 label_cross_project_descendants: With subprojects
942 label_cross_project_tree: With project tree
943 label_cross_project_tree: With project tree
943 label_cross_project_hierarchy: With project hierarchy
944 label_cross_project_hierarchy: With project hierarchy
944 label_cross_project_system: With all projects
945 label_cross_project_system: With all projects
945 label_gantt_progress_line: Progress line
946 label_gantt_progress_line: Progress line
946 label_visibility_private: to me only
947 label_visibility_private: to me only
947 label_visibility_roles: to these roles only
948 label_visibility_roles: to these roles only
948 label_visibility_public: to any users
949 label_visibility_public: to any users
949 label_link: Link
950 label_link: Link
950 label_only: only
951 label_only: only
951 label_drop_down_list: drop-down list
952 label_drop_down_list: drop-down list
952 label_checkboxes: checkboxes
953 label_checkboxes: checkboxes
953 label_radio_buttons: radio buttons
954 label_radio_buttons: radio buttons
954 label_link_values_to: Link values to URL
955 label_link_values_to: Link values to URL
955 label_custom_field_select_type: Select the type of object to which the custom field is to be attached
956 label_custom_field_select_type: Select the type of object to which the custom field is to be attached
956 label_check_for_updates: Check for updates
957 label_check_for_updates: Check for updates
957 label_latest_compatible_version: Latest compatible version
958 label_latest_compatible_version: Latest compatible version
958 label_unknown_plugin: Unknown plugin
959 label_unknown_plugin: Unknown plugin
959 label_add_projects: Add projects
960 label_add_projects: Add projects
960 label_users_visibility_all: All active users
961 label_users_visibility_all: All active users
961 label_users_visibility_members_of_visible_projects: Members of visible projects
962 label_users_visibility_members_of_visible_projects: Members of visible projects
962 label_edit_attachments: Edit attached files
963 label_edit_attachments: Edit attached files
963 label_link_copied_issue: Link copied issue
964 label_link_copied_issue: Link copied issue
964 label_ask: Ask
965 label_ask: Ask
965 label_search_attachments_yes: Search attachment filenames and descriptions
966 label_search_attachments_yes: Search attachment filenames and descriptions
966 label_search_attachments_no: Do not search attachments
967 label_search_attachments_no: Do not search attachments
967 label_search_attachments_only: Search attachments only
968 label_search_attachments_only: Search attachments only
968 label_search_open_issues_only: Open issues only
969 label_search_open_issues_only: Open issues only
969 label_email_address_plural: Emails
970 label_email_address_plural: Emails
970 label_email_address_add: Add email address
971 label_email_address_add: Add email address
971 label_enable_notifications: Enable notifications
972 label_enable_notifications: Enable notifications
972 label_disable_notifications: Disable notifications
973 label_disable_notifications: Disable notifications
973 label_blank_value: blank
974 label_blank_value: blank
974 label_parent_task_attributes: Parent tasks attributes
975 label_parent_task_attributes: Parent tasks attributes
975 label_parent_task_attributes_derived: Calculated from subtasks
976 label_parent_task_attributes_derived: Calculated from subtasks
976 label_parent_task_attributes_independent: Independent of subtasks
977 label_parent_task_attributes_independent: Independent of subtasks
977 label_time_entries_visibility_all: All time entries
978 label_time_entries_visibility_all: All time entries
978 label_time_entries_visibility_own: Time entries created by the user
979 label_time_entries_visibility_own: Time entries created by the user
979 label_member_management: Member management
980 label_member_management: Member management
980 label_member_management_all_roles: All roles
981 label_member_management_all_roles: All roles
981 label_member_management_selected_roles_only: Only these roles
982 label_member_management_selected_roles_only: Only these roles
982 label_import_issues: Import issues
983 label_import_issues: Import issues
983 label_select_file_to_import: Select the file to import
984 label_select_file_to_import: Select the file to import
984 label_fields_separator: Field separator
985 label_fields_separator: Field separator
985 label_fields_wrapper: Field wrapper
986 label_fields_wrapper: Field wrapper
986 label_encoding: Encoding
987 label_encoding: Encoding
987 label_comma_char: Comma
988 label_comma_char: Comma
988 label_semi_colon_char: Semicolon
989 label_semi_colon_char: Semicolon
989 label_quote_char: Quote
990 label_quote_char: Quote
990 label_double_quote_char: Double quote
991 label_double_quote_char: Double quote
991 label_fields_mapping: Fields mapping
992 label_fields_mapping: Fields mapping
992 label_file_content_preview: File content preview
993 label_file_content_preview: File content preview
993 label_create_missing_values: Create missing values
994 label_create_missing_values: Create missing values
994 label_api: API
995 label_api: API
995 label_field_format_enumeration: Key/value list
996 label_field_format_enumeration: Key/value list
996 label_default_values_for_new_users: Default values for new users
997 label_default_values_for_new_users: Default values for new users
997 label_relations: Relations
998 label_relations: Relations
998 label_new_project_issue_tab_enabled: Display the "New issue" tab
999 label_new_project_issue_tab_enabled: Display the "New issue" tab
999 label_new_object_tab_enabled: Display the "+" drop-down
1000 label_new_object_tab_enabled: Display the "+" drop-down
1000
1001
1001 button_login: Login
1002 button_login: Login
1002 button_submit: Submit
1003 button_submit: Submit
1003 button_save: Save
1004 button_save: Save
1004 button_check_all: Check all
1005 button_check_all: Check all
1005 button_uncheck_all: Uncheck all
1006 button_uncheck_all: Uncheck all
1006 button_collapse_all: Collapse all
1007 button_collapse_all: Collapse all
1007 button_expand_all: Expand all
1008 button_expand_all: Expand all
1008 button_delete: Delete
1009 button_delete: Delete
1009 button_create: Create
1010 button_create: Create
1010 button_create_and_continue: Create and continue
1011 button_create_and_continue: Create and continue
1011 button_test: Test
1012 button_test: Test
1012 button_edit: Edit
1013 button_edit: Edit
1013 button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
1014 button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}"
1014 button_add: Add
1015 button_add: Add
1015 button_change: Change
1016 button_change: Change
1016 button_apply: Apply
1017 button_apply: Apply
1017 button_clear: Clear
1018 button_clear: Clear
1018 button_lock: Lock
1019 button_lock: Lock
1019 button_unlock: Unlock
1020 button_unlock: Unlock
1020 button_download: Download
1021 button_download: Download
1021 button_list: List
1022 button_list: List
1022 button_view: View
1023 button_view: View
1023 button_move: Move
1024 button_move: Move
1024 button_move_and_follow: Move and follow
1025 button_move_and_follow: Move and follow
1025 button_back: Back
1026 button_back: Back
1026 button_cancel: Cancel
1027 button_cancel: Cancel
1027 button_activate: Activate
1028 button_activate: Activate
1028 button_sort: Sort
1029 button_sort: Sort
1029 button_log_time: Log time
1030 button_log_time: Log time
1030 button_rollback: Rollback to this version
1031 button_rollback: Rollback to this version
1031 button_watch: Watch
1032 button_watch: Watch
1032 button_unwatch: Unwatch
1033 button_unwatch: Unwatch
1033 button_reply: Reply
1034 button_reply: Reply
1034 button_archive: Archive
1035 button_archive: Archive
1035 button_unarchive: Unarchive
1036 button_unarchive: Unarchive
1036 button_reset: Reset
1037 button_reset: Reset
1037 button_rename: Rename
1038 button_rename: Rename
1038 button_change_password: Change password
1039 button_change_password: Change password
1039 button_copy: Copy
1040 button_copy: Copy
1040 button_copy_and_follow: Copy and follow
1041 button_copy_and_follow: Copy and follow
1041 button_annotate: Annotate
1042 button_annotate: Annotate
1042 button_update: Update
1043 button_update: Update
1043 button_configure: Configure
1044 button_configure: Configure
1044 button_quote: Quote
1045 button_quote: Quote
1045 button_duplicate: Duplicate
1046 button_duplicate: Duplicate
1046 button_show: Show
1047 button_show: Show
1047 button_hide: Hide
1048 button_hide: Hide
1048 button_edit_section: Edit this section
1049 button_edit_section: Edit this section
1049 button_export: Export
1050 button_export: Export
1050 button_delete_my_account: Delete my account
1051 button_delete_my_account: Delete my account
1051 button_close: Close
1052 button_close: Close
1052 button_reopen: Reopen
1053 button_reopen: Reopen
1053 button_import: Import
1054 button_import: Import
1054 button_filter: Filter
1055 button_filter: Filter
1055
1056
1056 status_active: active
1057 status_active: active
1057 status_registered: registered
1058 status_registered: registered
1058 status_locked: locked
1059 status_locked: locked
1059
1060
1060 project_status_active: active
1061 project_status_active: active
1061 project_status_closed: closed
1062 project_status_closed: closed
1062 project_status_archived: archived
1063 project_status_archived: archived
1063
1064
1064 version_status_open: open
1065 version_status_open: open
1065 version_status_locked: locked
1066 version_status_locked: locked
1066 version_status_closed: closed
1067 version_status_closed: closed
1067
1068
1068 field_active: Active
1069 field_active: Active
1069
1070
1070 text_select_mail_notifications: Select actions for which email notifications should be sent.
1071 text_select_mail_notifications: Select actions for which email notifications should be sent.
1071 text_regexp_info: eg. ^[A-Z0-9]+$
1072 text_regexp_info: eg. ^[A-Z0-9]+$
1072 text_min_max_length_info: 0 means no restriction
1073 text_min_max_length_info: 0 means no restriction
1073 text_project_destroy_confirmation: Are you sure you want to delete this project and related data?
1074 text_project_destroy_confirmation: Are you sure you want to delete this project and related data?
1074 text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted."
1075 text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted."
1075 text_workflow_edit: Select a role and a tracker to edit the workflow
1076 text_workflow_edit: Select a role and a tracker to edit the workflow
1076 text_are_you_sure: Are you sure?
1077 text_are_you_sure: Are you sure?
1077 text_journal_changed: "%{label} changed from %{old} to %{new}"
1078 text_journal_changed: "%{label} changed from %{old} to %{new}"
1078 text_journal_changed_no_detail: "%{label} updated"
1079 text_journal_changed_no_detail: "%{label} updated"
1079 text_journal_set_to: "%{label} set to %{value}"
1080 text_journal_set_to: "%{label} set to %{value}"
1080 text_journal_deleted: "%{label} deleted (%{old})"
1081 text_journal_deleted: "%{label} deleted (%{old})"
1081 text_journal_added: "%{label} %{value} added"
1082 text_journal_added: "%{label} %{value} added"
1082 text_tip_issue_begin_day: issue beginning this day
1083 text_tip_issue_begin_day: issue beginning this day
1083 text_tip_issue_end_day: issue ending this day
1084 text_tip_issue_end_day: issue ending this day
1084 text_tip_issue_begin_end_day: issue beginning and ending this day
1085 text_tip_issue_begin_end_day: issue beginning and ending this day
1085 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 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 text_caracters_maximum: "%{count} characters maximum."
1087 text_caracters_maximum: "%{count} characters maximum."
1087 text_caracters_minimum: "Must be at least %{count} characters long."
1088 text_caracters_minimum: "Must be at least %{count} characters long."
1088 text_length_between: "Length between %{min} and %{max} characters."
1089 text_length_between: "Length between %{min} and %{max} characters."
1089 text_tracker_no_workflow: No workflow defined for this tracker
1090 text_tracker_no_workflow: No workflow defined for this tracker
1090 text_unallowed_characters: Unallowed characters
1091 text_unallowed_characters: Unallowed characters
1091 text_comma_separated: Multiple values allowed (comma separated).
1092 text_comma_separated: Multiple values allowed (comma separated).
1092 text_line_separated: Multiple values allowed (one line for each value).
1093 text_line_separated: Multiple values allowed (one line for each value).
1093 text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
1094 text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
1094 text_issue_added: "Issue %{id} has been reported by %{author}."
1095 text_issue_added: "Issue %{id} has been reported by %{author}."
1095 text_issue_updated: "Issue %{id} has been updated by %{author}."
1096 text_issue_updated: "Issue %{id} has been updated by %{author}."
1096 text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content?
1097 text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content?
1097 text_issue_category_destroy_question: "Some issues (%{count}) are assigned to this category. What do you want to do?"
1098 text_issue_category_destroy_question: "Some issues (%{count}) are assigned to this category. What do you want to do?"
1098 text_issue_category_destroy_assignments: Remove category assignments
1099 text_issue_category_destroy_assignments: Remove category assignments
1099 text_issue_category_reassign_to: Reassign issues to this category
1100 text_issue_category_reassign_to: Reassign issues to this category
1100 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 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 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 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 text_load_default_configuration: Load the default configuration
1103 text_load_default_configuration: Load the default configuration
1103 text_status_changed_by_changeset: "Applied in changeset %{value}."
1104 text_status_changed_by_changeset: "Applied in changeset %{value}."
1104 text_time_logged_by_changeset: "Applied in changeset %{value}."
1105 text_time_logged_by_changeset: "Applied in changeset %{value}."
1105 text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s)?'
1106 text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s)?'
1106 text_issues_destroy_descendants_confirmation: "This will also delete %{count} subtask(s)."
1107 text_issues_destroy_descendants_confirmation: "This will also delete %{count} subtask(s)."
1107 text_time_entries_destroy_confirmation: 'Are you sure you want to delete the selected time entr(y/ies)?'
1108 text_time_entries_destroy_confirmation: 'Are you sure you want to delete the selected time entr(y/ies)?'
1108 text_select_project_modules: 'Select modules to enable for this project:'
1109 text_select_project_modules: 'Select modules to enable for this project:'
1109 text_default_administrator_account_changed: Default administrator account changed
1110 text_default_administrator_account_changed: Default administrator account changed
1110 text_file_repository_writable: Attachments directory writable
1111 text_file_repository_writable: Attachments directory writable
1111 text_plugin_assets_writable: Plugin assets directory writable
1112 text_plugin_assets_writable: Plugin assets directory writable
1112 text_rmagick_available: RMagick available (optional)
1113 text_rmagick_available: RMagick available (optional)
1113 text_convert_available: ImageMagick convert available (optional)
1114 text_convert_available: ImageMagick convert available (optional)
1114 text_destroy_time_entries_question: "%{hours} hours were reported on the issues you are about to delete. What do you want to do?"
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 text_destroy_time_entries: Delete reported hours
1116 text_destroy_time_entries: Delete reported hours
1116 text_assign_time_entries_to_project: Assign reported hours to the project
1117 text_assign_time_entries_to_project: Assign reported hours to the project
1117 text_reassign_time_entries: 'Reassign reported hours to this issue:'
1118 text_reassign_time_entries: 'Reassign reported hours to this issue:'
1118 text_user_wrote: "%{value} wrote:"
1119 text_user_wrote: "%{value} wrote:"
1119 text_enumeration_destroy_question: "%{count} objects are assigned to the value β€œ%{name}”."
1120 text_enumeration_destroy_question: "%{count} objects are assigned to the value β€œ%{name}”."
1120 text_enumeration_category_reassign_to: 'Reassign them to this value:'
1121 text_enumeration_category_reassign_to: 'Reassign them to this value:'
1121 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 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 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 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 text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
1124 text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
1124 text_custom_field_possible_values_info: 'One line for each value'
1125 text_custom_field_possible_values_info: 'One line for each value'
1125 text_wiki_page_destroy_question: "This page has %{descendants} child page(s) and descendant(s). What do you want to do?"
1126 text_wiki_page_destroy_question: "This page has %{descendants} child page(s) and descendant(s). What do you want to do?"
1126 text_wiki_page_nullify_children: "Keep child pages as root pages"
1127 text_wiki_page_nullify_children: "Keep child pages as root pages"
1127 text_wiki_page_destroy_children: "Delete child pages and all their descendants"
1128 text_wiki_page_destroy_children: "Delete child pages and all their descendants"
1128 text_wiki_page_reassign_children: "Reassign child pages to this parent page"
1129 text_wiki_page_reassign_children: "Reassign child pages to this parent page"
1129 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 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 text_zoom_in: Zoom in
1131 text_zoom_in: Zoom in
1131 text_zoom_out: Zoom out
1132 text_zoom_out: Zoom out
1132 text_warn_on_leaving_unsaved: "The current page contains unsaved text that will be lost if you leave this page."
1133 text_warn_on_leaving_unsaved: "The current page contains unsaved text that will be lost if you leave this page."
1133 text_scm_path_encoding_note: "Default: UTF-8"
1134 text_scm_path_encoding_note: "Default: UTF-8"
1134 text_subversion_repository_note: "Examples: file:///, http://, https://, svn://, svn+[tunnelscheme]://"
1135 text_subversion_repository_note: "Examples: file:///, http://, https://, svn://, svn+[tunnelscheme]://"
1135 text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo)
1136 text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo)
1136 text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo)
1137 text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo)
1137 text_scm_command: Command
1138 text_scm_command: Command
1138 text_scm_command_version: Version
1139 text_scm_command_version: Version
1139 text_scm_config: You can configure your SCM commands in config/configuration.yml. Please restart the application after editing it.
1140 text_scm_config: You can configure your SCM commands in config/configuration.yml. Please restart the application after editing it.
1140 text_scm_command_not_available: SCM command is not available. Please check settings on the administration panel.
1141 text_scm_command_not_available: SCM command is not available. Please check settings on the administration panel.
1141 text_issue_conflict_resolution_overwrite: "Apply my changes anyway (previous notes will be kept but some changes may be overwritten)"
1142 text_issue_conflict_resolution_overwrite: "Apply my changes anyway (previous notes will be kept but some changes may be overwritten)"
1142 text_issue_conflict_resolution_add_notes: "Add my notes and discard my other changes"
1143 text_issue_conflict_resolution_add_notes: "Add my notes and discard my other changes"
1143 text_issue_conflict_resolution_cancel: "Discard all my changes and redisplay %{link}"
1144 text_issue_conflict_resolution_cancel: "Discard all my changes and redisplay %{link}"
1144 text_account_destroy_confirmation: "Are you sure you want to proceed?\nYour account will be permanently deleted, with no way to reactivate it."
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 text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
1146 text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours."
1146 text_project_closed: This project is closed and read-only.
1147 text_project_closed: This project is closed and read-only.
1147 text_turning_multiple_off: "If you disable multiple values, multiple values will be removed in order to preserve only one value per item."
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 default_role_manager: Manager
1150 default_role_manager: Manager
1150 default_role_developer: Developer
1151 default_role_developer: Developer
1151 default_role_reporter: Reporter
1152 default_role_reporter: Reporter
1152 default_tracker_bug: Bug
1153 default_tracker_bug: Bug
1153 default_tracker_feature: Feature
1154 default_tracker_feature: Feature
1154 default_tracker_support: Support
1155 default_tracker_support: Support
1155 default_issue_status_new: New
1156 default_issue_status_new: New
1156 default_issue_status_in_progress: In Progress
1157 default_issue_status_in_progress: In Progress
1157 default_issue_status_resolved: Resolved
1158 default_issue_status_resolved: Resolved
1158 default_issue_status_feedback: Feedback
1159 default_issue_status_feedback: Feedback
1159 default_issue_status_closed: Closed
1160 default_issue_status_closed: Closed
1160 default_issue_status_rejected: Rejected
1161 default_issue_status_rejected: Rejected
1161 default_doc_category_user: User documentation
1162 default_doc_category_user: User documentation
1162 default_doc_category_tech: Technical documentation
1163 default_doc_category_tech: Technical documentation
1163 default_priority_low: Low
1164 default_priority_low: Low
1164 default_priority_normal: Normal
1165 default_priority_normal: Normal
1165 default_priority_high: High
1166 default_priority_high: High
1166 default_priority_urgent: Urgent
1167 default_priority_urgent: Urgent
1167 default_priority_immediate: Immediate
1168 default_priority_immediate: Immediate
1168 default_activity_design: Design
1169 default_activity_design: Design
1169 default_activity_development: Development
1170 default_activity_development: Development
1170
1171
1171 enumeration_issue_priorities: Issue priorities
1172 enumeration_issue_priorities: Issue priorities
1172 enumeration_doc_categories: Document categories
1173 enumeration_doc_categories: Document categories
1173 enumeration_activities: Activities (time tracking)
1174 enumeration_activities: Activities (time tracking)
1174 enumeration_system_activity: System Activity
1175 enumeration_system_activity: System Activity
1175 description_filter: Filter
1176 description_filter: Filter
1176 description_search: Searchfield
1177 description_search: Searchfield
1177 description_choose_project: Projects
1178 description_choose_project: Projects
1178 description_project_scope: Search scope
1179 description_project_scope: Search scope
1179 description_notes: Notes
1180 description_notes: Notes
1180 description_message_content: Message content
1181 description_message_content: Message content
1181 description_query_sort_criteria_attribute: Sort attribute
1182 description_query_sort_criteria_attribute: Sort attribute
1182 description_query_sort_criteria_direction: Sort direction
1183 description_query_sort_criteria_direction: Sort direction
1183 description_user_mail_notification: Mail notification settings
1184 description_user_mail_notification: Mail notification settings
1184 description_available_columns: Available Columns
1185 description_available_columns: Available Columns
1185 description_selected_columns: Selected Columns
1186 description_selected_columns: Selected Columns
1186 description_all_columns: All Columns
1187 description_all_columns: All Columns
1187 description_issue_category_reassign: Choose issue category
1188 description_issue_category_reassign: Choose issue category
1188 description_wiki_subpages_reassign: Choose new parent page
1189 description_wiki_subpages_reassign: Choose new parent page
1189 description_date_range_list: Choose range from list
1190 description_date_range_list: Choose range from list
1190 description_date_range_interval: Choose range by selecting start and end date
1191 description_date_range_interval: Choose range by selecting start and end date
1191 description_date_from: Enter start date
1192 description_date_from: Enter start date
1192 description_date_to: Enter end date
1193 description_date_to: Enter end date
1193 text_repository_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.'
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 # French translations for Ruby on Rails
1 # French translations for Ruby on Rails
2 # by Christian Lescuyer (christian@flyingcoders.com)
2 # by Christian Lescuyer (christian@flyingcoders.com)
3 # contributor: Sebastien Grosjean - ZenCocoon.com
3 # contributor: Sebastien Grosjean - ZenCocoon.com
4 # contributor: Thibaut Cuvelier - Developpez.com
4 # contributor: Thibaut Cuvelier - Developpez.com
5
5
6 fr:
6 fr:
7 direction: ltr
7 direction: ltr
8 date:
8 date:
9 formats:
9 formats:
10 default: "%d/%m/%Y"
10 default: "%d/%m/%Y"
11 short: "%e %b"
11 short: "%e %b"
12 long: "%e %B %Y"
12 long: "%e %B %Y"
13 long_ordinal: "%e %B %Y"
13 long_ordinal: "%e %B %Y"
14 only_day: "%e"
14 only_day: "%e"
15
15
16 day_names: [dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi]
16 day_names: [dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi]
17 abbr_day_names: [dim, lun, mar, mer, jeu, ven, sam]
17 abbr_day_names: [dim, lun, mar, mer, jeu, ven, sam]
18
18
19 # Don't forget the nil at the beginning; there's no such thing as a 0th month
19 # Don't forget the nil at the beginning; there's no such thing as a 0th month
20 month_names: [~, janvier, fΓ©vrier, mars, avril, mai, juin, juillet, aoΓ»t, septembre, octobre, novembre, dΓ©cembre]
20 month_names: [~, janvier, fΓ©vrier, mars, avril, mai, juin, juillet, aoΓ»t, septembre, octobre, novembre, dΓ©cembre]
21 abbr_month_names: [~, jan., fΓ©v., mar., avr., mai, juin, juil., aoΓ»t, sept., oct., nov., dΓ©c.]
21 abbr_month_names: [~, jan., fΓ©v., mar., avr., mai, juin, juil., aoΓ»t, sept., oct., nov., dΓ©c.]
22 # Used in date_select and datime_select.
22 # Used in date_select and datime_select.
23 order:
23 order:
24 - :day
24 - :day
25 - :month
25 - :month
26 - :year
26 - :year
27
27
28 time:
28 time:
29 formats:
29 formats:
30 default: "%d/%m/%Y %H:%M"
30 default: "%d/%m/%Y %H:%M"
31 time: "%H:%M"
31 time: "%H:%M"
32 short: "%d %b %H:%M"
32 short: "%d %b %H:%M"
33 long: "%A %d %B %Y %H:%M:%S %Z"
33 long: "%A %d %B %Y %H:%M:%S %Z"
34 long_ordinal: "%A %d %B %Y %H:%M:%S %Z"
34 long_ordinal: "%A %d %B %Y %H:%M:%S %Z"
35 only_second: "%S"
35 only_second: "%S"
36 am: 'am'
36 am: 'am'
37 pm: 'pm'
37 pm: 'pm'
38
38
39 datetime:
39 datetime:
40 distance_in_words:
40 distance_in_words:
41 half_a_minute: "30 secondes"
41 half_a_minute: "30 secondes"
42 less_than_x_seconds:
42 less_than_x_seconds:
43 zero: "moins d'une seconde"
43 zero: "moins d'une seconde"
44 one: "moins d'uneΒ seconde"
44 one: "moins d'uneΒ seconde"
45 other: "moins de %{count}Β secondes"
45 other: "moins de %{count}Β secondes"
46 x_seconds:
46 x_seconds:
47 one: "1Β seconde"
47 one: "1Β seconde"
48 other: "%{count}Β secondes"
48 other: "%{count}Β secondes"
49 less_than_x_minutes:
49 less_than_x_minutes:
50 zero: "moins d'une minute"
50 zero: "moins d'une minute"
51 one: "moins d'uneΒ minute"
51 one: "moins d'uneΒ minute"
52 other: "moins de %{count}Β minutes"
52 other: "moins de %{count}Β minutes"
53 x_minutes:
53 x_minutes:
54 one: "1Β minute"
54 one: "1Β minute"
55 other: "%{count}Β minutes"
55 other: "%{count}Β minutes"
56 about_x_hours:
56 about_x_hours:
57 one: "environ une heure"
57 one: "environ une heure"
58 other: "environ %{count}Β heures"
58 other: "environ %{count}Β heures"
59 x_hours:
59 x_hours:
60 one: "une heure"
60 one: "une heure"
61 other: "%{count}Β heures"
61 other: "%{count}Β heures"
62 x_days:
62 x_days:
63 one: "unΒ jour"
63 one: "unΒ jour"
64 other: "%{count}Β jours"
64 other: "%{count}Β jours"
65 about_x_months:
65 about_x_months:
66 one: "environ un mois"
66 one: "environ un mois"
67 other: "environ %{count}Β mois"
67 other: "environ %{count}Β mois"
68 x_months:
68 x_months:
69 one: "unΒ mois"
69 one: "unΒ mois"
70 other: "%{count}Β mois"
70 other: "%{count}Β mois"
71 about_x_years:
71 about_x_years:
72 one: "environ un an"
72 one: "environ un an"
73 other: "environ %{count}Β ans"
73 other: "environ %{count}Β ans"
74 over_x_years:
74 over_x_years:
75 one: "plus d'un an"
75 one: "plus d'un an"
76 other: "plus de %{count}Β ans"
76 other: "plus de %{count}Β ans"
77 almost_x_years:
77 almost_x_years:
78 one: "presqu'un an"
78 one: "presqu'un an"
79 other: "presque %{count} ans"
79 other: "presque %{count} ans"
80 prompts:
80 prompts:
81 year: "AnnΓ©e"
81 year: "AnnΓ©e"
82 month: "Mois"
82 month: "Mois"
83 day: "Jour"
83 day: "Jour"
84 hour: "Heure"
84 hour: "Heure"
85 minute: "Minute"
85 minute: "Minute"
86 second: "Seconde"
86 second: "Seconde"
87
87
88 number:
88 number:
89 format:
89 format:
90 precision: 3
90 precision: 3
91 separator: ','
91 separator: ','
92 delimiter: 'Β '
92 delimiter: 'Β '
93 currency:
93 currency:
94 format:
94 format:
95 unit: '€'
95 unit: '€'
96 precision: 2
96 precision: 2
97 format: '%nΒ %u'
97 format: '%nΒ %u'
98 human:
98 human:
99 format:
99 format:
100 precision: 3
100 precision: 3
101 storage_units:
101 storage_units:
102 format: "%n %u"
102 format: "%n %u"
103 units:
103 units:
104 byte:
104 byte:
105 one: "octet"
105 one: "octet"
106 other: "octets"
106 other: "octets"
107 kb: "ko"
107 kb: "ko"
108 mb: "Mo"
108 mb: "Mo"
109 gb: "Go"
109 gb: "Go"
110 tb: "To"
110 tb: "To"
111
111
112 support:
112 support:
113 array:
113 array:
114 sentence_connector: 'et'
114 sentence_connector: 'et'
115 skip_last_comma: true
115 skip_last_comma: true
116 word_connector: ", "
116 word_connector: ", "
117 two_words_connector: " et "
117 two_words_connector: " et "
118 last_word_connector: " et "
118 last_word_connector: " et "
119
119
120 activerecord:
120 activerecord:
121 errors:
121 errors:
122 template:
122 template:
123 header:
123 header:
124 one: "Impossible d'enregistrer %{model} : une erreur"
124 one: "Impossible d'enregistrer %{model} : une erreur"
125 other: "Impossible d'enregistrer %{model} : %{count} erreurs."
125 other: "Impossible d'enregistrer %{model} : %{count} erreurs."
126 body: "Veuillez vΓ©rifier les champs suivantsΒ :"
126 body: "Veuillez vΓ©rifier les champs suivantsΒ :"
127 messages:
127 messages:
128 inclusion: "n'est pas inclus(e) dans la liste"
128 inclusion: "n'est pas inclus(e) dans la liste"
129 exclusion: "n'est pas disponible"
129 exclusion: "n'est pas disponible"
130 invalid: "n'est pas valide"
130 invalid: "n'est pas valide"
131 confirmation: "ne concorde pas avec la confirmation"
131 confirmation: "ne concorde pas avec la confirmation"
132 accepted: "doit Γͺtre acceptΓ©(e)"
132 accepted: "doit Γͺtre acceptΓ©(e)"
133 empty: "doit Γͺtre renseignΓ©(e)"
133 empty: "doit Γͺtre renseignΓ©(e)"
134 blank: "doit Γͺtre renseignΓ©(e)"
134 blank: "doit Γͺtre renseignΓ©(e)"
135 too_long: "est trop long (pas plus de %{count} caractères)"
135 too_long: "est trop long (pas plus de %{count} caractères)"
136 too_short: "est trop court (au moins %{count} caractères)"
136 too_short: "est trop court (au moins %{count} caractères)"
137 wrong_length: "ne fait pas la bonne longueur (doit comporter %{count} caractères)"
137 wrong_length: "ne fait pas la bonne longueur (doit comporter %{count} caractères)"
138 taken: "est dΓ©jΓ  utilisΓ©"
138 taken: "est dΓ©jΓ  utilisΓ©"
139 not_a_number: "n'est pas un nombre"
139 not_a_number: "n'est pas un nombre"
140 not_a_date: "n'est pas une date valide"
140 not_a_date: "n'est pas une date valide"
141 greater_than: "doit Γͺtre supΓ©rieur Γ  %{count}"
141 greater_than: "doit Γͺtre supΓ©rieur Γ  %{count}"
142 greater_than_or_equal_to: "doit Γͺtre supΓ©rieur ou Γ©gal Γ  %{count}"
142 greater_than_or_equal_to: "doit Γͺtre supΓ©rieur ou Γ©gal Γ  %{count}"
143 equal_to: "doit Γͺtre Γ©gal Γ  %{count}"
143 equal_to: "doit Γͺtre Γ©gal Γ  %{count}"
144 less_than: "doit Γͺtre infΓ©rieur Γ  %{count}"
144 less_than: "doit Γͺtre infΓ©rieur Γ  %{count}"
145 less_than_or_equal_to: "doit Γͺtre infΓ©rieur ou Γ©gal Γ  %{count}"
145 less_than_or_equal_to: "doit Γͺtre infΓ©rieur ou Γ©gal Γ  %{count}"
146 odd: "doit Γͺtre impair"
146 odd: "doit Γͺtre impair"
147 even: "doit Γͺtre pair"
147 even: "doit Γͺtre pair"
148 greater_than_start_date: "doit Γͺtre postΓ©rieure Γ  la date de dΓ©but"
148 greater_than_start_date: "doit Γͺtre postΓ©rieure Γ  la date de dΓ©but"
149 not_same_project: "n'appartient pas au mΓͺme projet"
149 not_same_project: "n'appartient pas au mΓͺme projet"
150 circular_dependency: "Cette relation crΓ©erait une dΓ©pendance circulaire"
150 circular_dependency: "Cette relation crΓ©erait une dΓ©pendance circulaire"
151 cant_link_an_issue_with_a_descendant: "Une demande ne peut pas Γͺtre liΓ©e Γ  l'une de ses sous-tΓ’ches"
151 cant_link_an_issue_with_a_descendant: "Une demande ne peut pas Γͺtre liΓ©e Γ  l'une de ses sous-tΓ’ches"
152 earlier_than_minimum_start_date: "ne peut pas Γͺtre antΓ©rieure au %{date} Γ  cause des demandes qui prΓ©cΓ¨dent"
152 earlier_than_minimum_start_date: "ne peut pas Γͺtre antΓ©rieure au %{date} Γ  cause des demandes qui prΓ©cΓ¨dent"
153
153
154 actionview_instancetag_blank_option: Choisir
154 actionview_instancetag_blank_option: Choisir
155
155
156 general_text_No: 'Non'
156 general_text_No: 'Non'
157 general_text_Yes: 'Oui'
157 general_text_Yes: 'Oui'
158 general_text_no: 'non'
158 general_text_no: 'non'
159 general_text_yes: 'oui'
159 general_text_yes: 'oui'
160 general_lang_name: 'French (FranΓ§ais)'
160 general_lang_name: 'French (FranΓ§ais)'
161 general_csv_separator: ';'
161 general_csv_separator: ';'
162 general_csv_decimal_separator: ','
162 general_csv_decimal_separator: ','
163 general_csv_encoding: ISO-8859-1
163 general_csv_encoding: ISO-8859-1
164 general_pdf_fontname: freesans
164 general_pdf_fontname: freesans
165 general_pdf_monospaced_fontname: freemono
165 general_pdf_monospaced_fontname: freemono
166 general_first_day_of_week: '1'
166 general_first_day_of_week: '1'
167
167
168 notice_account_updated: Le compte a été mis à jour avec succès.
168 notice_account_updated: Le compte a été mis à jour avec succès.
169 notice_account_invalid_credentials: Identifiant ou mot de passe invalide.
169 notice_account_invalid_credentials: Identifiant ou mot de passe invalide.
170 notice_account_password_updated: Mot de passe mis à jour avec succès.
170 notice_account_password_updated: Mot de passe mis à jour avec succès.
171 notice_account_wrong_password: Mot de passe incorrect
171 notice_account_wrong_password: Mot de passe incorrect
172 notice_account_register_done: Un message contenant les instructions pour activer votre compte vous a Γ©tΓ© envoyΓ© Γ  l'adresse %{email}.
172 notice_account_register_done: Un message contenant les instructions pour activer votre compte vous a Γ©tΓ© envoyΓ© Γ  l'adresse %{email}.
173 notice_account_unknown_email: Aucun compte ne correspond Γ  cette adresse.
173 notice_account_unknown_email: Aucun compte ne correspond Γ  cette adresse.
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>.
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 notice_account_locked: Votre compte est verrouillΓ©.
175 notice_account_locked: Votre compte est verrouillΓ©.
176 notice_can_t_change_password: Ce compte utilise une authentification externe. Impossible de changer le mot de passe.
176 notice_can_t_change_password: Ce compte utilise une authentification externe. Impossible de changer le mot de passe.
177 notice_account_lost_email_sent: Un message contenant les instructions pour choisir un nouveau mot de passe vous a Γ©tΓ© envoyΓ©.
177 notice_account_lost_email_sent: Un message contenant les instructions pour choisir un nouveau mot de passe vous a Γ©tΓ© envoyΓ©.
178 notice_account_activated: Votre compte a Γ©tΓ© activΓ©. Vous pouvez Γ  prΓ©sent vous connecter.
178 notice_account_activated: Votre compte a Γ©tΓ© activΓ©. Vous pouvez Γ  prΓ©sent vous connecter.
179 notice_successful_create: Création effectuée avec succès.
179 notice_successful_create: Création effectuée avec succès.
180 notice_successful_update: Mise à jour effectuée avec succès.
180 notice_successful_update: Mise à jour effectuée avec succès.
181 notice_successful_delete: Suppression effectuée avec succès.
181 notice_successful_delete: Suppression effectuée avec succès.
182 notice_successful_connection: Connexion rΓ©ussie.
182 notice_successful_connection: Connexion rΓ©ussie.
183 notice_file_not_found: "La page Γ  laquelle vous souhaitez accΓ©der n'existe pas ou a Γ©tΓ© supprimΓ©e."
183 notice_file_not_found: "La page Γ  laquelle vous souhaitez accΓ©der n'existe pas ou a Γ©tΓ© supprimΓ©e."
184 notice_locking_conflict: Les donnΓ©es ont Γ©tΓ© mises Γ  jour par un autre utilisateur. Mise Γ  jour impossible.
184 notice_locking_conflict: Les donnΓ©es ont Γ©tΓ© mises Γ  jour par un autre utilisateur. Mise Γ  jour impossible.
185 notice_not_authorized: "Vous n'Γͺtes pas autorisΓ© Γ  accΓ©der Γ  cette page."
185 notice_not_authorized: "Vous n'Γͺtes pas autorisΓ© Γ  accΓ©der Γ  cette page."
186 notice_not_authorized_archived_project: Le projet auquel vous tentez d'accΓ©der a Γ©tΓ© archivΓ©.
186 notice_not_authorized_archived_project: Le projet auquel vous tentez d'accΓ©der a Γ©tΓ© archivΓ©.
187 notice_email_sent: "Un email a Γ©tΓ© envoyΓ© Γ  %{value}"
187 notice_email_sent: "Un email a Γ©tΓ© envoyΓ© Γ  %{value}"
188 notice_email_error: "Erreur lors de l'envoi de l'email (%{value})"
188 notice_email_error: "Erreur lors de l'envoi de l'email (%{value})"
189 notice_feeds_access_key_reseted: "Votre clé d'accès aux flux Atom a été réinitialisée."
189 notice_feeds_access_key_reseted: "Votre clé d'accès aux flux Atom a été réinitialisée."
190 notice_api_access_key_reseted: Votre clé d'accès API a été réinitialisée.
190 notice_api_access_key_reseted: Votre clé d'accès API a été réinitialisée.
191 notice_failed_to_save_issues: "%{count} demande(s) sur les %{total} sΓ©lectionnΓ©es n'ont pas pu Γͺtre mise(s) Γ  jour : %{ids}."
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 notice_failed_to_save_time_entries: "%{count} temps passΓ©(s) sur les %{total} sΓ©lectionnΓ©s n'ont pas pu Γͺtre mis Γ  jour: %{ids}."
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 notice_failed_to_save_members: "Erreur lors de la sauvegarde des membres: %{errors}."
193 notice_failed_to_save_members: "Erreur lors de la sauvegarde des membres: %{errors}."
194 notice_no_issue_selected: "Aucune demande sΓ©lectionnΓ©e ! Cochez les demandes que vous voulez mettre Γ  jour."
194 notice_no_issue_selected: "Aucune demande sΓ©lectionnΓ©e ! Cochez les demandes que vous voulez mettre Γ  jour."
195 notice_account_pending: "Votre compte a été créé et attend l'approbation de l'administrateur."
195 notice_account_pending: "Votre compte a été créé et attend l'approbation de l'administrateur."
196 notice_default_data_loaded: Paramétrage par défaut chargé avec succès.
196 notice_default_data_loaded: Paramétrage par défaut chargé avec succès.
197 notice_unable_delete_version: Impossible de supprimer cette version.
197 notice_unable_delete_version: Impossible de supprimer cette version.
198 notice_unable_delete_time_entry: Impossible de supprimer le temps passΓ©.
198 notice_unable_delete_time_entry: Impossible de supprimer le temps passΓ©.
199 notice_issue_done_ratios_updated: L'avancement des demandes a Γ©tΓ© mis Γ  jour.
199 notice_issue_done_ratios_updated: L'avancement des demandes a Γ©tΓ© mis Γ  jour.
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})"
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 notice_issue_successful_create: "Demande %{id} créée."
201 notice_issue_successful_create: "Demande %{id} créée."
202 notice_issue_update_conflict: "La demande a Γ©tΓ© mise Γ  jour par un autre utilisateur pendant que vous la modifiez."
202 notice_issue_update_conflict: "La demande a Γ©tΓ© mise Γ  jour par un autre utilisateur pendant que vous la modifiez."
203 notice_account_deleted: "Votre compte a Γ©tΓ© dΓ©finitivement supprimΓ©."
203 notice_account_deleted: "Votre compte a Γ©tΓ© dΓ©finitivement supprimΓ©."
204 notice_user_successful_create: "Utilisateur %{id} créé."
204 notice_user_successful_create: "Utilisateur %{id} créé."
205 notice_new_password_must_be_different: Votre nouveau mot de passe doit Γͺtre diffΓ©rent de votre mot de passe actuel
205 notice_new_password_must_be_different: Votre nouveau mot de passe doit Γͺtre diffΓ©rent de votre mot de passe actuel
206 notice_import_finished: "%{count} Γ©lΓ©ments ont Γ©tΓ© importΓ©(s)"
206 notice_import_finished: "%{count} Γ©lΓ©ments ont Γ©tΓ© importΓ©(s)"
207 notice_import_finished_with_errors: "%{count} Γ©lΓ©ment(s) sur %{total} n'ont pas pu Γͺtre importΓ©(s)"
207 notice_import_finished_with_errors: "%{count} Γ©lΓ©ment(s) sur %{total} n'ont pas pu Γͺtre importΓ©(s)"
208
208
209 error_can_t_load_default_data: "Une erreur s'est produite lors du chargement du paramΓ©trage : %{value}"
209 error_can_t_load_default_data: "Une erreur s'est produite lors du chargement du paramΓ©trage : %{value}"
210 error_scm_not_found: "L'entrΓ©e et/ou la rΓ©vision demandΓ©e n'existe pas dans le dΓ©pΓ΄t."
210 error_scm_not_found: "L'entrΓ©e et/ou la rΓ©vision demandΓ©e n'existe pas dans le dΓ©pΓ΄t."
211 error_scm_command_failed: "Une erreur s'est produite lors de l'accès au dépôt : %{value}"
211 error_scm_command_failed: "Une erreur s'est produite lors de l'accès au dépôt : %{value}"
212 error_scm_annotate: "L'entrΓ©e n'existe pas ou ne peut pas Γͺtre annotΓ©e."
212 error_scm_annotate: "L'entrΓ©e n'existe pas ou ne peut pas Γͺtre annotΓ©e."
213 error_scm_annotate_big_text_file: Cette entrΓ©e ne peut pas Γͺtre annotΓ©e car elle excΓ¨de la taille maximale.
213 error_scm_annotate_big_text_file: Cette entrΓ©e ne peut pas Γͺtre annotΓ©e car elle excΓ¨de la taille maximale.
214 error_issue_not_found_in_project: "La demande n'existe pas ou n'appartient pas Γ  ce projet"
214 error_issue_not_found_in_project: "La demande n'existe pas ou n'appartient pas Γ  ce projet"
215 error_no_tracker_in_project: "Aucun tracker n'est associΓ© Γ  ce projet. VΓ©rifier la configuration du projet."
215 error_no_tracker_in_project: "Aucun tracker n'est associΓ© Γ  ce projet. VΓ©rifier la configuration du projet."
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)."
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 error_can_not_delete_custom_field: Impossible de supprimer le champ personnalisΓ©
217 error_can_not_delete_custom_field: Impossible de supprimer le champ personnalisΓ©
218 error_can_not_delete_tracker: Ce tracker contient des demandes et ne peut pas Γͺtre supprimΓ©.
218 error_can_not_delete_tracker: Ce tracker contient des demandes et ne peut pas Γͺtre supprimΓ©.
219 error_can_not_remove_role: Ce rΓ΄le est utilisΓ© et ne peut pas Γͺtre supprimΓ©.
219 error_can_not_remove_role: Ce rΓ΄le est utilisΓ© et ne peut pas Γͺtre supprimΓ©.
220 error_can_not_reopen_issue_on_closed_version: 'Une demande assignΓ©e Γ  une version fermΓ©e ne peut pas Γͺtre rΓ©ouverte'
220 error_can_not_reopen_issue_on_closed_version: 'Une demande assignΓ©e Γ  une version fermΓ©e ne peut pas Γͺtre rΓ©ouverte'
221 error_can_not_archive_project: "Ce projet ne peut pas Γͺtre archivΓ©"
221 error_can_not_archive_project: "Ce projet ne peut pas Γͺtre archivΓ©"
222 error_issue_done_ratios_not_updated: L'avancement des demandes n'a pas pu Γͺtre mis Γ  jour.
222 error_issue_done_ratios_not_updated: L'avancement des demandes n'a pas pu Γͺtre mis Γ  jour.
223 error_workflow_copy_source: 'Veuillez sΓ©lectionner un tracker et/ou un rΓ΄le source'
223 error_workflow_copy_source: 'Veuillez sΓ©lectionner un tracker et/ou un rΓ΄le source'
224 error_workflow_copy_target: 'Veuillez sΓ©lectionner les trackers et rΓ΄les cibles'
224 error_workflow_copy_target: 'Veuillez sΓ©lectionner les trackers et rΓ΄les cibles'
225 error_unable_delete_issue_status: Impossible de supprimer le statut de demande
225 error_unable_delete_issue_status: Impossible de supprimer le statut de demande
226 error_unable_to_connect: Connexion impossible (%{value})
226 error_unable_to_connect: Connexion impossible (%{value})
227 error_attachment_too_big: Ce fichier ne peut pas Γͺtre attachΓ© car il excΓ¨de la taille maximale autorisΓ©e (%{max_size})
227 error_attachment_too_big: Ce fichier ne peut pas Γͺtre attachΓ© car il excΓ¨de la taille maximale autorisΓ©e (%{max_size})
228 error_session_expired: "Votre session a expirΓ©. Veuillez vous reconnecter."
228 error_session_expired: "Votre session a expirΓ©. Veuillez vous reconnecter."
229 warning_attachments_not_saved: "%{count} fichier(s) n'ont pas pu Γͺtre sauvegardΓ©s."
229 warning_attachments_not_saved: "%{count} fichier(s) n'ont pas pu Γͺtre sauvegardΓ©s."
230 error_password_expired: "Votre mot de passe a expirΓ© ou nΓ©cessite d'Γͺtre changΓ©."
230 error_password_expired: "Votre mot de passe a expirΓ© ou nΓ©cessite d'Γͺtre changΓ©."
231 error_invalid_file_encoding: "Le fichier n'est pas un fichier %{encoding} valide"
231 error_invalid_file_encoding: "Le fichier n'est pas un fichier %{encoding} valide"
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"
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 error_can_not_read_import_file: "Une erreur est survenue lors de la lecture du fichier Γ  importer"
233 error_can_not_read_import_file: "Une erreur est survenue lors de la lecture du fichier Γ  importer"
234 error_attachment_extension_not_allowed: "L'extension %{extension} n'est pas autorisΓ©e"
234 error_attachment_extension_not_allowed: "L'extension %{extension} n'est pas autorisΓ©e"
235 error_ldap_bind_credentials: "Identifiant ou mot de passe LDAP incorrect"
235 error_ldap_bind_credentials: "Identifiant ou mot de passe LDAP incorrect"
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"
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 mail_subject_lost_password: "Votre mot de passe %{value}"
239 mail_subject_lost_password: "Votre mot de passe %{value}"
239 mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :'
240 mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :'
240 mail_subject_register: "Activation de votre compte %{value}"
241 mail_subject_register: "Activation de votre compte %{value}"
241 mail_body_register: 'Pour activer votre compte, cliquez sur le lien suivant :'
242 mail_body_register: 'Pour activer votre compte, cliquez sur le lien suivant :'
242 mail_body_account_information_external: "Vous pouvez utiliser votre compte %{value} pour vous connecter."
243 mail_body_account_information_external: "Vous pouvez utiliser votre compte %{value} pour vous connecter."
243 mail_body_account_information: Paramètres de connexion de votre compte
244 mail_body_account_information: Paramètres de connexion de votre compte
244 mail_subject_account_activation_request: "Demande d'activation d'un compte %{value}"
245 mail_subject_account_activation_request: "Demande d'activation d'un compte %{value}"
245 mail_body_account_activation_request: "Un nouvel utilisateur (%{value}) s'est inscrit. Son compte nΓ©cessite votre approbation :"
246 mail_body_account_activation_request: "Un nouvel utilisateur (%{value}) s'est inscrit. Son compte nΓ©cessite votre approbation :"
246 mail_subject_reminder: "%{count} demande(s) arrivent Γ  Γ©chΓ©ance (%{days})"
247 mail_subject_reminder: "%{count} demande(s) arrivent Γ  Γ©chΓ©ance (%{days})"
247 mail_body_reminder: "%{count} demande(s) qui vous sont assignΓ©es arrivent Γ  Γ©chΓ©ance dans les %{days} prochains jours :"
248 mail_body_reminder: "%{count} demande(s) qui vous sont assignΓ©es arrivent Γ  Γ©chΓ©ance dans les %{days} prochains jours :"
248 mail_subject_wiki_content_added: "Page wiki '%{id}' ajoutΓ©e"
249 mail_subject_wiki_content_added: "Page wiki '%{id}' ajoutΓ©e"
249 mail_body_wiki_content_added: "La page wiki '%{id}' a Γ©tΓ© ajoutΓ©e par %{author}."
250 mail_body_wiki_content_added: "La page wiki '%{id}' a Γ©tΓ© ajoutΓ©e par %{author}."
250 mail_subject_wiki_content_updated: "Page wiki '%{id}' mise Γ  jour"
251 mail_subject_wiki_content_updated: "Page wiki '%{id}' mise Γ  jour"
251 mail_body_wiki_content_updated: "La page wiki '%{id}' a Γ©tΓ© mise Γ  jour par %{author}."
252 mail_body_wiki_content_updated: "La page wiki '%{id}' a Γ©tΓ© mise Γ  jour par %{author}."
252 mail_body_settings_updated: "Les paramètres suivants ont été modifiés :"
253 mail_body_settings_updated: "Les paramètres suivants ont été modifiés :"
253 mail_body_password_updated: "Votre mot de passe a Γ©tΓ© changΓ©."
254 mail_body_password_updated: "Votre mot de passe a Γ©tΓ© changΓ©."
254
255
255 field_name: Nom
256 field_name: Nom
256 field_description: Description
257 field_description: Description
257 field_summary: RΓ©sumΓ©
258 field_summary: RΓ©sumΓ©
258 field_is_required: Obligatoire
259 field_is_required: Obligatoire
259 field_firstname: PrΓ©nom
260 field_firstname: PrΓ©nom
260 field_lastname: Nom
261 field_lastname: Nom
261 field_mail: Email
262 field_mail: Email
262 field_address: Email
263 field_address: Email
263 field_filename: Fichier
264 field_filename: Fichier
264 field_filesize: Taille
265 field_filesize: Taille
265 field_downloads: TΓ©lΓ©chargements
266 field_downloads: TΓ©lΓ©chargements
266 field_author: Auteur
267 field_author: Auteur
267 field_created_on: Créé
268 field_created_on: Créé
268 field_updated_on: Mis-Γ -jour
269 field_updated_on: Mis-Γ -jour
269 field_closed_on: FermΓ©
270 field_closed_on: FermΓ©
270 field_field_format: Format
271 field_field_format: Format
271 field_is_for_all: Pour tous les projets
272 field_is_for_all: Pour tous les projets
272 field_possible_values: Valeurs possibles
273 field_possible_values: Valeurs possibles
273 field_regexp: Expression régulière
274 field_regexp: Expression régulière
274 field_min_length: Longueur minimum
275 field_min_length: Longueur minimum
275 field_max_length: Longueur maximum
276 field_max_length: Longueur maximum
276 field_value: Valeur
277 field_value: Valeur
277 field_category: CatΓ©gorie
278 field_category: CatΓ©gorie
278 field_title: Titre
279 field_title: Titre
279 field_project: Projet
280 field_project: Projet
280 field_issue: Demande
281 field_issue: Demande
281 field_status: Statut
282 field_status: Statut
282 field_notes: Notes
283 field_notes: Notes
283 field_is_closed: Demande fermΓ©e
284 field_is_closed: Demande fermΓ©e
284 field_is_default: Valeur par dΓ©faut
285 field_is_default: Valeur par dΓ©faut
285 field_tracker: Tracker
286 field_tracker: Tracker
286 field_subject: Sujet
287 field_subject: Sujet
287 field_due_date: EchΓ©ance
288 field_due_date: EchΓ©ance
288 field_assigned_to: AssignΓ© Γ 
289 field_assigned_to: AssignΓ© Γ 
289 field_priority: PrioritΓ©
290 field_priority: PrioritΓ©
290 field_fixed_version: Version cible
291 field_fixed_version: Version cible
291 field_user: Utilisateur
292 field_user: Utilisateur
292 field_principal: Principal
293 field_principal: Principal
293 field_role: RΓ΄le
294 field_role: RΓ΄le
294 field_homepage: Site web
295 field_homepage: Site web
295 field_is_public: Public
296 field_is_public: Public
296 field_parent: Sous-projet de
297 field_parent: Sous-projet de
297 field_is_in_roadmap: Demandes affichΓ©es dans la roadmap
298 field_is_in_roadmap: Demandes affichΓ©es dans la roadmap
298 field_login: Identifiant
299 field_login: Identifiant
299 field_mail_notification: Notifications par mail
300 field_mail_notification: Notifications par mail
300 field_admin: Administrateur
301 field_admin: Administrateur
301 field_last_login_on: Dernière connexion
302 field_last_login_on: Dernière connexion
302 field_language: Langue
303 field_language: Langue
303 field_effective_date: Date
304 field_effective_date: Date
304 field_password: Mot de passe
305 field_password: Mot de passe
305 field_new_password: Nouveau mot de passe
306 field_new_password: Nouveau mot de passe
306 field_password_confirmation: Confirmation
307 field_password_confirmation: Confirmation
307 field_version: Version
308 field_version: Version
308 field_type: Type
309 field_type: Type
309 field_host: HΓ΄te
310 field_host: HΓ΄te
310 field_port: Port
311 field_port: Port
311 field_account: Compte
312 field_account: Compte
312 field_base_dn: Base DN
313 field_base_dn: Base DN
313 field_attr_login: Attribut Identifiant
314 field_attr_login: Attribut Identifiant
314 field_attr_firstname: Attribut PrΓ©nom
315 field_attr_firstname: Attribut PrΓ©nom
315 field_attr_lastname: Attribut Nom
316 field_attr_lastname: Attribut Nom
316 field_attr_mail: Attribut Email
317 field_attr_mail: Attribut Email
317 field_onthefly: CrΓ©ation des utilisateurs Γ  la volΓ©e
318 field_onthefly: CrΓ©ation des utilisateurs Γ  la volΓ©e
318 field_start_date: DΓ©but
319 field_start_date: DΓ©but
319 field_done_ratio: "% rΓ©alisΓ©"
320 field_done_ratio: "% rΓ©alisΓ©"
320 field_auth_source: Mode d'authentification
321 field_auth_source: Mode d'authentification
321 field_hide_mail: Cacher mon adresse mail
322 field_hide_mail: Cacher mon adresse mail
322 field_comments: Commentaire
323 field_comments: Commentaire
323 field_url: URL
324 field_url: URL
324 field_start_page: Page de dΓ©marrage
325 field_start_page: Page de dΓ©marrage
325 field_subproject: Sous-projet
326 field_subproject: Sous-projet
326 field_hours: Heures
327 field_hours: Heures
327 field_activity: ActivitΓ©
328 field_activity: ActivitΓ©
328 field_spent_on: Date
329 field_spent_on: Date
329 field_identifier: Identifiant
330 field_identifier: Identifiant
330 field_is_filter: UtilisΓ© comme filtre
331 field_is_filter: UtilisΓ© comme filtre
331 field_issue_to: Demande liΓ©e
332 field_issue_to: Demande liΓ©e
332 field_delay: Retard
333 field_delay: Retard
333 field_assignable: Demandes assignables Γ  ce rΓ΄le
334 field_assignable: Demandes assignables Γ  ce rΓ΄le
334 field_redirect_existing_links: Rediriger les liens existants
335 field_redirect_existing_links: Rediriger les liens existants
335 field_estimated_hours: Temps estimΓ©
336 field_estimated_hours: Temps estimΓ©
336 field_column_names: Colonnes
337 field_column_names: Colonnes
337 field_time_entries: Temps passΓ©
338 field_time_entries: Temps passΓ©
338 field_time_zone: Fuseau horaire
339 field_time_zone: Fuseau horaire
339 field_searchable: UtilisΓ© pour les recherches
340 field_searchable: UtilisΓ© pour les recherches
340 field_default_value: Valeur par dΓ©faut
341 field_default_value: Valeur par dΓ©faut
341 field_comments_sorting: Afficher les commentaires
342 field_comments_sorting: Afficher les commentaires
342 field_parent_title: Page parent
343 field_parent_title: Page parent
343 field_editable: Modifiable
344 field_editable: Modifiable
344 field_watcher: Observateur
345 field_watcher: Observateur
345 field_identity_url: URL OpenID
346 field_identity_url: URL OpenID
346 field_content: Contenu
347 field_content: Contenu
347 field_group_by: Grouper par
348 field_group_by: Grouper par
348 field_sharing: Partage
349 field_sharing: Partage
349 field_parent_issue: TΓ’che parente
350 field_parent_issue: TΓ’che parente
350 field_member_of_group: Groupe de l'assignΓ©
351 field_member_of_group: Groupe de l'assignΓ©
351 field_assigned_to_role: RΓ΄le de l'assignΓ©
352 field_assigned_to_role: RΓ΄le de l'assignΓ©
352 field_text: Champ texte
353 field_text: Champ texte
353 field_visible: Visible
354 field_visible: Visible
354 field_warn_on_leaving_unsaved: "M'avertir lorsque je quitte une page contenant du texte non sauvegardΓ©"
355 field_warn_on_leaving_unsaved: "M'avertir lorsque je quitte une page contenant du texte non sauvegardΓ©"
355 field_issues_visibility: VisibilitΓ© des demandes
356 field_issues_visibility: VisibilitΓ© des demandes
356 field_is_private: PrivΓ©e
357 field_is_private: PrivΓ©e
357 field_commit_logs_encoding: Encodage des messages de commit
358 field_commit_logs_encoding: Encodage des messages de commit
358 field_scm_path_encoding: Encodage des chemins
359 field_scm_path_encoding: Encodage des chemins
359 field_path_to_repository: Chemin du dΓ©pΓ΄t
360 field_path_to_repository: Chemin du dΓ©pΓ΄t
360 field_root_directory: RΓ©pertoire racine
361 field_root_directory: RΓ©pertoire racine
361 field_cvsroot: CVSROOT
362 field_cvsroot: CVSROOT
362 field_cvs_module: Module
363 field_cvs_module: Module
363 field_repository_is_default: DΓ©pΓ΄t principal
364 field_repository_is_default: DΓ©pΓ΄t principal
364 field_multiple: Valeurs multiples
365 field_multiple: Valeurs multiples
365 field_auth_source_ldap_filter: Filtre LDAP
366 field_auth_source_ldap_filter: Filtre LDAP
366 field_core_fields: Champs standards
367 field_core_fields: Champs standards
367 field_timeout: "Timeout (en secondes)"
368 field_timeout: "Timeout (en secondes)"
368 field_board_parent: Forum parent
369 field_board_parent: Forum parent
369 field_private_notes: Notes privΓ©es
370 field_private_notes: Notes privΓ©es
370 field_inherit_members: HΓ©riter les membres
371 field_inherit_members: HΓ©riter les membres
371 field_generate_password: GΓ©nΓ©rer un mot de passe
372 field_generate_password: GΓ©nΓ©rer un mot de passe
372 field_must_change_passwd: Doit changer de mot de passe Γ  la prochaine connexion
373 field_must_change_passwd: Doit changer de mot de passe Γ  la prochaine connexion
373 field_default_status: Statut par dΓ©faut
374 field_default_status: Statut par dΓ©faut
374 field_users_visibility: VisibilitΓ© des utilisateurs
375 field_users_visibility: VisibilitΓ© des utilisateurs
375 field_time_entries_visibility: VisibilitΓ© du temps passΓ©
376 field_time_entries_visibility: VisibilitΓ© du temps passΓ©
376 field_total_estimated_hours: Temps estimΓ© total
377 field_total_estimated_hours: Temps estimΓ© total
377 field_default_version: Version par dΓ©faut
378 field_default_version: Version par dΓ©faut
378
379
379 setting_app_title: Titre de l'application
380 setting_app_title: Titre de l'application
380 setting_app_subtitle: Sous-titre de l'application
381 setting_app_subtitle: Sous-titre de l'application
381 setting_welcome_text: Texte d'accueil
382 setting_welcome_text: Texte d'accueil
382 setting_default_language: Langue par dΓ©faut
383 setting_default_language: Langue par dΓ©faut
383 setting_login_required: Authentification obligatoire
384 setting_login_required: Authentification obligatoire
384 setting_self_registration: Inscription des nouveaux utilisateurs
385 setting_self_registration: Inscription des nouveaux utilisateurs
385 setting_attachment_max_size: Taille maximale des fichiers
386 setting_attachment_max_size: Taille maximale des fichiers
386 setting_issues_export_limit: Limite d'exportation des demandes
387 setting_issues_export_limit: Limite d'exportation des demandes
387 setting_mail_from: Adresse d'Γ©mission
388 setting_mail_from: Adresse d'Γ©mission
388 setting_bcc_recipients: Destinataires en copie cachΓ©e (cci)
389 setting_bcc_recipients: Destinataires en copie cachΓ©e (cci)
389 setting_plain_text_mail: Mail en texte brut (non HTML)
390 setting_plain_text_mail: Mail en texte brut (non HTML)
390 setting_host_name: Nom d'hΓ΄te et chemin
391 setting_host_name: Nom d'hΓ΄te et chemin
391 setting_text_formatting: Formatage du texte
392 setting_text_formatting: Formatage du texte
392 setting_wiki_compression: Compression de l'historique des pages wiki
393 setting_wiki_compression: Compression de l'historique des pages wiki
393 setting_feeds_limit: Nombre maximal d'Γ©lΓ©ments dans les flux Atom
394 setting_feeds_limit: Nombre maximal d'Γ©lΓ©ments dans les flux Atom
394 setting_default_projects_public: DΓ©finir les nouveaux projets comme publics par dΓ©faut
395 setting_default_projects_public: DΓ©finir les nouveaux projets comme publics par dΓ©faut
395 setting_autofetch_changesets: RΓ©cupΓ©ration automatique des commits
396 setting_autofetch_changesets: RΓ©cupΓ©ration automatique des commits
396 setting_sys_api_enabled: Activer les WS pour la gestion des dΓ©pΓ΄ts
397 setting_sys_api_enabled: Activer les WS pour la gestion des dΓ©pΓ΄ts
397 setting_commit_ref_keywords: Mots-clΓ©s de rΓ©fΓ©rencement
398 setting_commit_ref_keywords: Mots-clΓ©s de rΓ©fΓ©rencement
398 setting_commit_fix_keywords: Mots-clΓ©s de rΓ©solution
399 setting_commit_fix_keywords: Mots-clΓ©s de rΓ©solution
399 setting_autologin: DurΓ©e maximale de connexion automatique
400 setting_autologin: DurΓ©e maximale de connexion automatique
400 setting_date_format: Format de date
401 setting_date_format: Format de date
401 setting_time_format: Format d'heure
402 setting_time_format: Format d'heure
402 setting_cross_project_issue_relations: Autoriser les relations entre demandes de diffΓ©rents projets
403 setting_cross_project_issue_relations: Autoriser les relations entre demandes de diffΓ©rents projets
403 setting_cross_project_subtasks: Autoriser les sous-tΓ’ches dans des projets diffΓ©rents
404 setting_cross_project_subtasks: Autoriser les sous-tΓ’ches dans des projets diffΓ©rents
404 setting_issue_list_default_columns: Colonnes affichΓ©es par dΓ©faut sur la liste des demandes
405 setting_issue_list_default_columns: Colonnes affichΓ©es par dΓ©faut sur la liste des demandes
405 setting_repositories_encodings: Encodages des fichiers et des dΓ©pΓ΄ts
406 setting_repositories_encodings: Encodages des fichiers et des dΓ©pΓ΄ts
406 setting_emails_header: En-tΓͺte des emails
407 setting_emails_header: En-tΓͺte des emails
407 setting_emails_footer: Pied-de-page des emails
408 setting_emails_footer: Pied-de-page des emails
408 setting_protocol: Protocole
409 setting_protocol: Protocole
409 setting_per_page_options: Options d'objets affichΓ©s par page
410 setting_per_page_options: Options d'objets affichΓ©s par page
410 setting_user_format: Format d'affichage des utilisateurs
411 setting_user_format: Format d'affichage des utilisateurs
411 setting_activity_days_default: Nombre de jours affichΓ©s sur l'activitΓ© des projets
412 setting_activity_days_default: Nombre de jours affichΓ©s sur l'activitΓ© des projets
412 setting_display_subprojects_issues: Afficher par dΓ©faut les demandes des sous-projets sur les projets principaux
413 setting_display_subprojects_issues: Afficher par dΓ©faut les demandes des sous-projets sur les projets principaux
413 setting_enabled_scm: SCM activΓ©s
414 setting_enabled_scm: SCM activΓ©s
414 setting_mail_handler_body_delimiters: "Tronquer les emails après l'une de ces lignes"
415 setting_mail_handler_body_delimiters: "Tronquer les emails après l'une de ces lignes"
415 setting_mail_handler_api_enabled: "Activer le WS pour la rΓ©ception d'emails"
416 setting_mail_handler_api_enabled: "Activer le WS pour la rΓ©ception d'emails"
416 setting_mail_handler_api_key: ClΓ© de protection de l'API
417 setting_mail_handler_api_key: ClΓ© de protection de l'API
417 setting_sequential_project_identifiers: GΓ©nΓ©rer des identifiants de projet sΓ©quentiels
418 setting_sequential_project_identifiers: GΓ©nΓ©rer des identifiants de projet sΓ©quentiels
418 setting_gravatar_enabled: Afficher les Gravatar des utilisateurs
419 setting_gravatar_enabled: Afficher les Gravatar des utilisateurs
419 setting_gravatar_default: Image Gravatar par dΓ©faut
420 setting_gravatar_default: Image Gravatar par dΓ©faut
420 setting_diff_max_lines_displayed: Nombre maximum de lignes de diff affichΓ©es
421 setting_diff_max_lines_displayed: Nombre maximum de lignes de diff affichΓ©es
421 setting_file_max_size_displayed: Taille maximum des fichiers texte affichΓ©s en ligne
422 setting_file_max_size_displayed: Taille maximum des fichiers texte affichΓ©s en ligne
422 setting_repository_log_display_limit: "Nombre maximum de rΓ©visions affichΓ©es sur l'historique d'un fichier"
423 setting_repository_log_display_limit: "Nombre maximum de rΓ©visions affichΓ©es sur l'historique d'un fichier"
423 setting_openid: "Autoriser l'authentification et l'enregistrement OpenID"
424 setting_openid: "Autoriser l'authentification et l'enregistrement OpenID"
424 setting_password_max_age: Expiration des mots de passe après
425 setting_password_max_age: Expiration des mots de passe après
425 setting_password_min_length: Longueur minimum des mots de passe
426 setting_password_min_length: Longueur minimum des mots de passe
426 setting_new_project_user_role_id: RΓ΄le donnΓ© Γ  un utilisateur non-administrateur qui crΓ©e un projet
427 setting_new_project_user_role_id: RΓ΄le donnΓ© Γ  un utilisateur non-administrateur qui crΓ©e un projet
427 setting_default_projects_modules: Modules activΓ©s par dΓ©faut pour les nouveaux projets
428 setting_default_projects_modules: Modules activΓ©s par dΓ©faut pour les nouveaux projets
428 setting_issue_done_ratio: Calcul de l'avancement des demandes
429 setting_issue_done_ratio: Calcul de l'avancement des demandes
429 setting_issue_done_ratio_issue_field: 'Utiliser le champ % effectuΓ©'
430 setting_issue_done_ratio_issue_field: 'Utiliser le champ % effectuΓ©'
430 setting_issue_done_ratio_issue_status: Utiliser le statut
431 setting_issue_done_ratio_issue_status: Utiliser le statut
431 setting_start_of_week: Jour de dΓ©but des calendriers
432 setting_start_of_week: Jour de dΓ©but des calendriers
432 setting_rest_api_enabled: Activer l'API REST
433 setting_rest_api_enabled: Activer l'API REST
433 setting_cache_formatted_text: Mettre en cache le texte formatΓ©
434 setting_cache_formatted_text: Mettre en cache le texte formatΓ©
434 setting_default_notification_option: Option de notification par dΓ©faut
435 setting_default_notification_option: Option de notification par dΓ©faut
435 setting_commit_logtime_enabled: Permettre la saisie de temps
436 setting_commit_logtime_enabled: Permettre la saisie de temps
436 setting_commit_logtime_activity_id: ActivitΓ© pour le temps saisi
437 setting_commit_logtime_activity_id: ActivitΓ© pour le temps saisi
437 setting_gantt_items_limit: Nombre maximum d'Γ©lΓ©ments affichΓ©s sur le gantt
438 setting_gantt_items_limit: Nombre maximum d'Γ©lΓ©ments affichΓ©s sur le gantt
438 setting_issue_group_assignment: Permettre l'assignation des demandes aux groupes
439 setting_issue_group_assignment: Permettre l'assignation des demandes aux groupes
439 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 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 setting_commit_cross_project_ref: Permettre le rΓ©fΓ©rencement et la rΓ©solution des demandes de tous les autres projets
441 setting_commit_cross_project_ref: Permettre le rΓ©fΓ©rencement et la rΓ©solution des demandes de tous les autres projets
441 setting_unsubscribe: Permettre aux utilisateurs de supprimer leur propre compte
442 setting_unsubscribe: Permettre aux utilisateurs de supprimer leur propre compte
442 setting_session_lifetime: DurΓ©e de vie maximale des sessions
443 setting_session_lifetime: DurΓ©e de vie maximale des sessions
443 setting_session_timeout: DurΓ©e maximale d'inactivitΓ©
444 setting_session_timeout: DurΓ©e maximale d'inactivitΓ©
444 setting_thumbnails_enabled: Afficher les vignettes des images
445 setting_thumbnails_enabled: Afficher les vignettes des images
445 setting_thumbnails_size: Taille des vignettes (en pixels)
446 setting_thumbnails_size: Taille des vignettes (en pixels)
446 setting_non_working_week_days: Jours non travaillΓ©s
447 setting_non_working_week_days: Jours non travaillΓ©s
447 setting_jsonp_enabled: Activer le support JSONP
448 setting_jsonp_enabled: Activer le support JSONP
448 setting_default_projects_tracker_ids: Trackers par dΓ©faut pour les nouveaux projets
449 setting_default_projects_tracker_ids: Trackers par dΓ©faut pour les nouveaux projets
449 setting_mail_handler_excluded_filenames: Exclure les fichiers attachΓ©s par leur nom
450 setting_mail_handler_excluded_filenames: Exclure les fichiers attachΓ©s par leur nom
450 setting_force_default_language_for_anonymous: Forcer la langue par dΓ©fault pour les utilisateurs anonymes
451 setting_force_default_language_for_anonymous: Forcer la langue par dΓ©fault pour les utilisateurs anonymes
451 setting_force_default_language_for_loggedin: Forcer la langue par dΓ©fault pour les utilisateurs identifiΓ©s
452 setting_force_default_language_for_loggedin: Forcer la langue par dΓ©fault pour les utilisateurs identifiΓ©s
452 setting_link_copied_issue: Lier les demandes lors de la copie
453 setting_link_copied_issue: Lier les demandes lors de la copie
453 setting_max_additional_emails: Nombre maximal d'adresses email additionnelles
454 setting_max_additional_emails: Nombre maximal d'adresses email additionnelles
454 setting_search_results_per_page: RΓ©sultats de recherche affichΓ©s par page
455 setting_search_results_per_page: RΓ©sultats de recherche affichΓ©s par page
455 setting_attachment_extensions_allowed: Extensions autorisΓ©es
456 setting_attachment_extensions_allowed: Extensions autorisΓ©es
456 setting_attachment_extensions_denied: Extensions non autorisΓ©es
457 setting_attachment_extensions_denied: Extensions non autorisΓ©es
457 setting_sys_api_key: ClΓ© de protection de l'API
458 setting_sys_api_key: ClΓ© de protection de l'API
458 setting_lost_password: Autoriser la rΓ©initialisation par email de mot de passe perdu
459 setting_lost_password: Autoriser la rΓ©initialisation par email de mot de passe perdu
459 setting_new_item_menu_tab: Onglet de crΓ©ation d'objets dans le menu du project
460 setting_new_item_menu_tab: Onglet de crΓ©ation d'objets dans le menu du project
460
461
461 permission_add_project: CrΓ©er un projet
462 permission_add_project: CrΓ©er un projet
462 permission_add_subprojects: CrΓ©er des sous-projets
463 permission_add_subprojects: CrΓ©er des sous-projets
463 permission_edit_project: Modifier le projet
464 permission_edit_project: Modifier le projet
464 permission_close_project: Fermer / rΓ©ouvrir le projet
465 permission_close_project: Fermer / rΓ©ouvrir le projet
465 permission_select_project_modules: Choisir les modules
466 permission_select_project_modules: Choisir les modules
466 permission_manage_members: GΓ©rer les membres
467 permission_manage_members: GΓ©rer les membres
467 permission_manage_project_activities: GΓ©rer les activitΓ©s
468 permission_manage_project_activities: GΓ©rer les activitΓ©s
468 permission_manage_versions: GΓ©rer les versions
469 permission_manage_versions: GΓ©rer les versions
469 permission_manage_categories: GΓ©rer les catΓ©gories de demandes
470 permission_manage_categories: GΓ©rer les catΓ©gories de demandes
470 permission_view_issues: Voir les demandes
471 permission_view_issues: Voir les demandes
471 permission_add_issues: CrΓ©er des demandes
472 permission_add_issues: CrΓ©er des demandes
472 permission_edit_issues: Modifier les demandes
473 permission_edit_issues: Modifier les demandes
473 permission_copy_issues: Copier les demandes
474 permission_copy_issues: Copier les demandes
474 permission_manage_issue_relations: GΓ©rer les relations
475 permission_manage_issue_relations: GΓ©rer les relations
475 permission_set_issues_private: Rendre les demandes publiques ou privΓ©es
476 permission_set_issues_private: Rendre les demandes publiques ou privΓ©es
476 permission_set_own_issues_private: Rendre ses propres demandes publiques ou privΓ©es
477 permission_set_own_issues_private: Rendre ses propres demandes publiques ou privΓ©es
477 permission_add_issue_notes: Ajouter des notes
478 permission_add_issue_notes: Ajouter des notes
478 permission_edit_issue_notes: Modifier les notes
479 permission_edit_issue_notes: Modifier les notes
479 permission_edit_own_issue_notes: Modifier ses propres notes
480 permission_edit_own_issue_notes: Modifier ses propres notes
480 permission_view_private_notes: Voir les notes privΓ©es
481 permission_view_private_notes: Voir les notes privΓ©es
481 permission_set_notes_private: Rendre les notes privΓ©es
482 permission_set_notes_private: Rendre les notes privΓ©es
482 permission_move_issues: DΓ©placer les demandes
483 permission_move_issues: DΓ©placer les demandes
483 permission_delete_issues: Supprimer les demandes
484 permission_delete_issues: Supprimer les demandes
484 permission_manage_public_queries: GΓ©rer les requΓͺtes publiques
485 permission_manage_public_queries: GΓ©rer les requΓͺtes publiques
485 permission_save_queries: Sauvegarder les requΓͺtes
486 permission_save_queries: Sauvegarder les requΓͺtes
486 permission_view_gantt: Voir le gantt
487 permission_view_gantt: Voir le gantt
487 permission_view_calendar: Voir le calendrier
488 permission_view_calendar: Voir le calendrier
488 permission_view_issue_watchers: Voir la liste des observateurs
489 permission_view_issue_watchers: Voir la liste des observateurs
489 permission_add_issue_watchers: Ajouter des observateurs
490 permission_add_issue_watchers: Ajouter des observateurs
490 permission_delete_issue_watchers: Supprimer des observateurs
491 permission_delete_issue_watchers: Supprimer des observateurs
491 permission_log_time: Saisir le temps passΓ©
492 permission_log_time: Saisir le temps passΓ©
492 permission_view_time_entries: Voir le temps passΓ©
493 permission_view_time_entries: Voir le temps passΓ©
493 permission_edit_time_entries: Modifier les temps passΓ©s
494 permission_edit_time_entries: Modifier les temps passΓ©s
494 permission_edit_own_time_entries: Modifier son propre temps passΓ©
495 permission_edit_own_time_entries: Modifier son propre temps passΓ©
495 permission_manage_news: GΓ©rer les annonces
496 permission_manage_news: GΓ©rer les annonces
496 permission_comment_news: Commenter les annonces
497 permission_comment_news: Commenter les annonces
497 permission_view_documents: Voir les documents
498 permission_view_documents: Voir les documents
498 permission_add_documents: Ajouter des documents
499 permission_add_documents: Ajouter des documents
499 permission_edit_documents: Modifier les documents
500 permission_edit_documents: Modifier les documents
500 permission_delete_documents: Supprimer les documents
501 permission_delete_documents: Supprimer les documents
501 permission_manage_files: GΓ©rer les fichiers
502 permission_manage_files: GΓ©rer les fichiers
502 permission_view_files: Voir les fichiers
503 permission_view_files: Voir les fichiers
503 permission_manage_wiki: GΓ©rer le wiki
504 permission_manage_wiki: GΓ©rer le wiki
504 permission_rename_wiki_pages: Renommer les pages
505 permission_rename_wiki_pages: Renommer les pages
505 permission_delete_wiki_pages: Supprimer les pages
506 permission_delete_wiki_pages: Supprimer les pages
506 permission_view_wiki_pages: Voir le wiki
507 permission_view_wiki_pages: Voir le wiki
507 permission_view_wiki_edits: "Voir l'historique des modifications"
508 permission_view_wiki_edits: "Voir l'historique des modifications"
508 permission_edit_wiki_pages: Modifier les pages
509 permission_edit_wiki_pages: Modifier les pages
509 permission_delete_wiki_pages_attachments: Supprimer les fichiers joints
510 permission_delete_wiki_pages_attachments: Supprimer les fichiers joints
510 permission_protect_wiki_pages: ProtΓ©ger les pages
511 permission_protect_wiki_pages: ProtΓ©ger les pages
511 permission_manage_repository: GΓ©rer le dΓ©pΓ΄t de sources
512 permission_manage_repository: GΓ©rer le dΓ©pΓ΄t de sources
512 permission_browse_repository: Parcourir les sources
513 permission_browse_repository: Parcourir les sources
513 permission_view_changesets: Voir les rΓ©visions
514 permission_view_changesets: Voir les rΓ©visions
514 permission_commit_access: Droit de commit
515 permission_commit_access: Droit de commit
515 permission_manage_boards: GΓ©rer les forums
516 permission_manage_boards: GΓ©rer les forums
516 permission_view_messages: Voir les messages
517 permission_view_messages: Voir les messages
517 permission_add_messages: Poster un message
518 permission_add_messages: Poster un message
518 permission_edit_messages: Modifier les messages
519 permission_edit_messages: Modifier les messages
519 permission_edit_own_messages: Modifier ses propres messages
520 permission_edit_own_messages: Modifier ses propres messages
520 permission_delete_messages: Supprimer les messages
521 permission_delete_messages: Supprimer les messages
521 permission_delete_own_messages: Supprimer ses propres messages
522 permission_delete_own_messages: Supprimer ses propres messages
522 permission_export_wiki_pages: Exporter les pages
523 permission_export_wiki_pages: Exporter les pages
523 permission_manage_subtasks: GΓ©rer les sous-tΓ’ches
524 permission_manage_subtasks: GΓ©rer les sous-tΓ’ches
524 permission_manage_related_issues: GΓ©rer les demandes associΓ©es
525 permission_manage_related_issues: GΓ©rer les demandes associΓ©es
525 permission_import_issues: Importer des demandes
526 permission_import_issues: Importer des demandes
526
527
527 project_module_issue_tracking: Suivi des demandes
528 project_module_issue_tracking: Suivi des demandes
528 project_module_time_tracking: Suivi du temps passΓ©
529 project_module_time_tracking: Suivi du temps passΓ©
529 project_module_news: Publication d'annonces
530 project_module_news: Publication d'annonces
530 project_module_documents: Publication de documents
531 project_module_documents: Publication de documents
531 project_module_files: Publication de fichiers
532 project_module_files: Publication de fichiers
532 project_module_wiki: Wiki
533 project_module_wiki: Wiki
533 project_module_repository: DΓ©pΓ΄t de sources
534 project_module_repository: DΓ©pΓ΄t de sources
534 project_module_boards: Forums de discussion
535 project_module_boards: Forums de discussion
535 project_module_calendar: Calendrier
536 project_module_calendar: Calendrier
536 project_module_gantt: Gantt
537 project_module_gantt: Gantt
537
538
538 label_user: Utilisateur
539 label_user: Utilisateur
539 label_user_plural: Utilisateurs
540 label_user_plural: Utilisateurs
540 label_user_new: Nouvel utilisateur
541 label_user_new: Nouvel utilisateur
541 label_user_anonymous: Anonyme
542 label_user_anonymous: Anonyme
542 label_project: Projet
543 label_project: Projet
543 label_project_new: Nouveau projet
544 label_project_new: Nouveau projet
544 label_project_plural: Projets
545 label_project_plural: Projets
545 label_x_projects:
546 label_x_projects:
546 zero: aucun projet
547 zero: aucun projet
547 one: un projet
548 one: un projet
548 other: "%{count} projets"
549 other: "%{count} projets"
549 label_project_all: Tous les projets
550 label_project_all: Tous les projets
550 label_project_latest: Derniers projets
551 label_project_latest: Derniers projets
551 label_issue: Demande
552 label_issue: Demande
552 label_issue_new: Nouvelle demande
553 label_issue_new: Nouvelle demande
553 label_issue_plural: Demandes
554 label_issue_plural: Demandes
554 label_issue_view_all: Voir toutes les demandes
555 label_issue_view_all: Voir toutes les demandes
555 label_issues_by: "Demandes par %{value}"
556 label_issues_by: "Demandes par %{value}"
556 label_issue_added: Demande ajoutΓ©e
557 label_issue_added: Demande ajoutΓ©e
557 label_issue_updated: Demande mise Γ  jour
558 label_issue_updated: Demande mise Γ  jour
558 label_issue_note_added: Note ajoutΓ©e
559 label_issue_note_added: Note ajoutΓ©e
559 label_issue_status_updated: Statut changΓ©
560 label_issue_status_updated: Statut changΓ©
560 label_issue_assigned_to_updated: AssignΓ© changΓ©
561 label_issue_assigned_to_updated: AssignΓ© changΓ©
561 label_issue_priority_updated: PrioritΓ© changΓ©e
562 label_issue_priority_updated: PrioritΓ© changΓ©e
562 label_document: Document
563 label_document: Document
563 label_document_new: Nouveau document
564 label_document_new: Nouveau document
564 label_document_plural: Documents
565 label_document_plural: Documents
565 label_document_added: Document ajoutΓ©
566 label_document_added: Document ajoutΓ©
566 label_role: RΓ΄le
567 label_role: RΓ΄le
567 label_role_plural: RΓ΄les
568 label_role_plural: RΓ΄les
568 label_role_new: Nouveau rΓ΄le
569 label_role_new: Nouveau rΓ΄le
569 label_role_and_permissions: RΓ΄les et permissions
570 label_role_and_permissions: RΓ΄les et permissions
570 label_role_anonymous: Anonyme
571 label_role_anonymous: Anonyme
571 label_role_non_member: Non membre
572 label_role_non_member: Non membre
572 label_member: Membre
573 label_member: Membre
573 label_member_new: Nouveau membre
574 label_member_new: Nouveau membre
574 label_member_plural: Membres
575 label_member_plural: Membres
575 label_tracker: Tracker
576 label_tracker: Tracker
576 label_tracker_plural: Trackers
577 label_tracker_plural: Trackers
577 label_tracker_all: Tous les trackers
578 label_tracker_all: Tous les trackers
578 label_tracker_new: Nouveau tracker
579 label_tracker_new: Nouveau tracker
579 label_workflow: Workflow
580 label_workflow: Workflow
580 label_issue_status: Statut de demandes
581 label_issue_status: Statut de demandes
581 label_issue_status_plural: Statuts de demandes
582 label_issue_status_plural: Statuts de demandes
582 label_issue_status_new: Nouveau statut
583 label_issue_status_new: Nouveau statut
583 label_issue_category: CatΓ©gorie de demandes
584 label_issue_category: CatΓ©gorie de demandes
584 label_issue_category_plural: CatΓ©gories de demandes
585 label_issue_category_plural: CatΓ©gories de demandes
585 label_issue_category_new: Nouvelle catΓ©gorie
586 label_issue_category_new: Nouvelle catΓ©gorie
586 label_custom_field: Champ personnalisΓ©
587 label_custom_field: Champ personnalisΓ©
587 label_custom_field_plural: Champs personnalisΓ©s
588 label_custom_field_plural: Champs personnalisΓ©s
588 label_custom_field_new: Nouveau champ personnalisΓ©
589 label_custom_field_new: Nouveau champ personnalisΓ©
589 label_enumerations: Listes de valeurs
590 label_enumerations: Listes de valeurs
590 label_enumeration_new: Nouvelle valeur
591 label_enumeration_new: Nouvelle valeur
591 label_information: Information
592 label_information: Information
592 label_information_plural: Informations
593 label_information_plural: Informations
593 label_please_login: Identification
594 label_please_login: Identification
594 label_register: S'enregistrer
595 label_register: S'enregistrer
595 label_login_with_open_id_option: S'authentifier avec OpenID
596 label_login_with_open_id_option: S'authentifier avec OpenID
596 label_password_lost: Mot de passe perdu
597 label_password_lost: Mot de passe perdu
597 label_password_required: Confirmez votre mot de passe pour continuer
598 label_password_required: Confirmez votre mot de passe pour continuer
598 label_home: Accueil
599 label_home: Accueil
599 label_my_page: Ma page
600 label_my_page: Ma page
600 label_my_account: Mon compte
601 label_my_account: Mon compte
601 label_my_projects: Mes projets
602 label_my_projects: Mes projets
602 label_my_page_block: Blocs disponibles
603 label_my_page_block: Blocs disponibles
603 label_administration: Administration
604 label_administration: Administration
604 label_login: Connexion
605 label_login: Connexion
605 label_logout: DΓ©connexion
606 label_logout: DΓ©connexion
606 label_help: Aide
607 label_help: Aide
607 label_reported_issues: Demandes soumises
608 label_reported_issues: Demandes soumises
608 label_assigned_issues: Demandes assignΓ©es
609 label_assigned_issues: Demandes assignΓ©es
609 label_assigned_to_me_issues: Demandes qui me sont assignΓ©es
610 label_assigned_to_me_issues: Demandes qui me sont assignΓ©es
610 label_last_login: Dernière connexion
611 label_last_login: Dernière connexion
611 label_registered_on: Inscrit le
612 label_registered_on: Inscrit le
612 label_activity: ActivitΓ©
613 label_activity: ActivitΓ©
613 label_overall_activity: ActivitΓ© globale
614 label_overall_activity: ActivitΓ© globale
614 label_user_activity: "ActivitΓ© de %{value}"
615 label_user_activity: "ActivitΓ© de %{value}"
615 label_new: Nouveau
616 label_new: Nouveau
616 label_logged_as: ConnectΓ© en tant que
617 label_logged_as: ConnectΓ© en tant que
617 label_environment: Environnement
618 label_environment: Environnement
618 label_authentication: Authentification
619 label_authentication: Authentification
619 label_auth_source: Mode d'authentification
620 label_auth_source: Mode d'authentification
620 label_auth_source_new: Nouveau mode d'authentification
621 label_auth_source_new: Nouveau mode d'authentification
621 label_auth_source_plural: Modes d'authentification
622 label_auth_source_plural: Modes d'authentification
622 label_subproject_plural: Sous-projets
623 label_subproject_plural: Sous-projets
623 label_subproject_new: Nouveau sous-projet
624 label_subproject_new: Nouveau sous-projet
624 label_and_its_subprojects: "%{value} et ses sous-projets"
625 label_and_its_subprojects: "%{value} et ses sous-projets"
625 label_min_max_length: Longueurs mini - maxi
626 label_min_max_length: Longueurs mini - maxi
626 label_list: Liste
627 label_list: Liste
627 label_date: Date
628 label_date: Date
628 label_integer: Entier
629 label_integer: Entier
629 label_float: Nombre dΓ©cimal
630 label_float: Nombre dΓ©cimal
630 label_boolean: BoolΓ©en
631 label_boolean: BoolΓ©en
631 label_string: Texte
632 label_string: Texte
632 label_text: Texte long
633 label_text: Texte long
633 label_attribute: Attribut
634 label_attribute: Attribut
634 label_attribute_plural: Attributs
635 label_attribute_plural: Attributs
635 label_no_data: Aucune donnΓ©e Γ  afficher
636 label_no_data: Aucune donnΓ©e Γ  afficher
636 label_change_status: Changer le statut
637 label_change_status: Changer le statut
637 label_history: Historique
638 label_history: Historique
638 label_attachment: Fichier
639 label_attachment: Fichier
639 label_attachment_new: Nouveau fichier
640 label_attachment_new: Nouveau fichier
640 label_attachment_delete: Supprimer le fichier
641 label_attachment_delete: Supprimer le fichier
641 label_attachment_plural: Fichiers
642 label_attachment_plural: Fichiers
642 label_file_added: Fichier ajoutΓ©
643 label_file_added: Fichier ajoutΓ©
643 label_report: Rapport
644 label_report: Rapport
644 label_report_plural: Rapports
645 label_report_plural: Rapports
645 label_news: Annonce
646 label_news: Annonce
646 label_news_new: Nouvelle annonce
647 label_news_new: Nouvelle annonce
647 label_news_plural: Annonces
648 label_news_plural: Annonces
648 label_news_latest: Dernières annonces
649 label_news_latest: Dernières annonces
649 label_news_view_all: Voir toutes les annonces
650 label_news_view_all: Voir toutes les annonces
650 label_news_added: Annonce ajoutΓ©e
651 label_news_added: Annonce ajoutΓ©e
651 label_news_comment_added: Commentaire ajoutΓ© Γ  une annonce
652 label_news_comment_added: Commentaire ajoutΓ© Γ  une annonce
652 label_settings: Configuration
653 label_settings: Configuration
653 label_overview: AperΓ§u
654 label_overview: AperΓ§u
654 label_version: Version
655 label_version: Version
655 label_version_new: Nouvelle version
656 label_version_new: Nouvelle version
656 label_version_plural: Versions
657 label_version_plural: Versions
657 label_close_versions: Fermer les versions terminΓ©es
658 label_close_versions: Fermer les versions terminΓ©es
658 label_confirmation: Confirmation
659 label_confirmation: Confirmation
659 label_export_to: 'Formats disponibles :'
660 label_export_to: 'Formats disponibles :'
660 label_read: Lire...
661 label_read: Lire...
661 label_public_projects: Projets publics
662 label_public_projects: Projets publics
662 label_open_issues: ouvert
663 label_open_issues: ouvert
663 label_open_issues_plural: ouverts
664 label_open_issues_plural: ouverts
664 label_closed_issues: fermΓ©
665 label_closed_issues: fermΓ©
665 label_closed_issues_plural: fermΓ©s
666 label_closed_issues_plural: fermΓ©s
666 label_x_open_issues_abbr:
667 label_x_open_issues_abbr:
667 zero: 0 ouverte
668 zero: 0 ouverte
668 one: 1 ouverte
669 one: 1 ouverte
669 other: "%{count} ouvertes"
670 other: "%{count} ouvertes"
670 label_x_closed_issues_abbr:
671 label_x_closed_issues_abbr:
671 zero: 0 fermΓ©e
672 zero: 0 fermΓ©e
672 one: 1 fermΓ©e
673 one: 1 fermΓ©e
673 other: "%{count} fermΓ©es"
674 other: "%{count} fermΓ©es"
674 label_x_issues:
675 label_x_issues:
675 zero: 0 demande
676 zero: 0 demande
676 one: 1 demande
677 one: 1 demande
677 other: "%{count} demandes"
678 other: "%{count} demandes"
678 label_total: Total
679 label_total: Total
679 label_total_plural: Totaux
680 label_total_plural: Totaux
680 label_total_time: Temps total
681 label_total_time: Temps total
681 label_permissions: Permissions
682 label_permissions: Permissions
682 label_current_status: Statut actuel
683 label_current_status: Statut actuel
683 label_new_statuses_allowed: Nouveaux statuts autorisΓ©s
684 label_new_statuses_allowed: Nouveaux statuts autorisΓ©s
684 label_all: tous
685 label_all: tous
685 label_any: tous
686 label_any: tous
686 label_none: aucun
687 label_none: aucun
687 label_nobody: personne
688 label_nobody: personne
688 label_next: Suivant
689 label_next: Suivant
689 label_previous: PrΓ©cΓ©dent
690 label_previous: PrΓ©cΓ©dent
690 label_used_by: UtilisΓ© par
691 label_used_by: UtilisΓ© par
691 label_details: DΓ©tails
692 label_details: DΓ©tails
692 label_add_note: Ajouter une note
693 label_add_note: Ajouter une note
693 label_calendar: Calendrier
694 label_calendar: Calendrier
694 label_months_from: mois depuis
695 label_months_from: mois depuis
695 label_gantt: Gantt
696 label_gantt: Gantt
696 label_internal: Interne
697 label_internal: Interne
697 label_last_changes: "%{count} derniers changements"
698 label_last_changes: "%{count} derniers changements"
698 label_change_view_all: Voir tous les changements
699 label_change_view_all: Voir tous les changements
699 label_personalize_page: Personnaliser cette page
700 label_personalize_page: Personnaliser cette page
700 label_comment: Commentaire
701 label_comment: Commentaire
701 label_comment_plural: Commentaires
702 label_comment_plural: Commentaires
702 label_x_comments:
703 label_x_comments:
703 zero: aucun commentaire
704 zero: aucun commentaire
704 one: un commentaire
705 one: un commentaire
705 other: "%{count} commentaires"
706 other: "%{count} commentaires"
706 label_comment_add: Ajouter un commentaire
707 label_comment_add: Ajouter un commentaire
707 label_comment_added: Commentaire ajoutΓ©
708 label_comment_added: Commentaire ajoutΓ©
708 label_comment_delete: Supprimer les commentaires
709 label_comment_delete: Supprimer les commentaires
709 label_query: Rapport personnalisΓ©
710 label_query: Rapport personnalisΓ©
710 label_query_plural: Rapports personnalisΓ©s
711 label_query_plural: Rapports personnalisΓ©s
711 label_query_new: Nouveau rapport
712 label_query_new: Nouveau rapport
712 label_my_queries: Mes rapports personnalisΓ©s
713 label_my_queries: Mes rapports personnalisΓ©s
713 label_filter_add: Ajouter le filtre
714 label_filter_add: Ajouter le filtre
714 label_filter_plural: Filtres
715 label_filter_plural: Filtres
715 label_equals: Γ©gal
716 label_equals: Γ©gal
716 label_not_equals: diffΓ©rent
717 label_not_equals: diffΓ©rent
717 label_in_less_than: dans moins de
718 label_in_less_than: dans moins de
718 label_in_more_than: dans plus de
719 label_in_more_than: dans plus de
719 label_in_the_next_days: dans les prochains jours
720 label_in_the_next_days: dans les prochains jours
720 label_in_the_past_days: dans les derniers jours
721 label_in_the_past_days: dans les derniers jours
721 label_greater_or_equal: '>='
722 label_greater_or_equal: '>='
722 label_less_or_equal: '<='
723 label_less_or_equal: '<='
723 label_between: entre
724 label_between: entre
724 label_in: dans
725 label_in: dans
725 label_today: aujourd'hui
726 label_today: aujourd'hui
726 label_all_time: toute la pΓ©riode
727 label_all_time: toute la pΓ©riode
727 label_yesterday: hier
728 label_yesterday: hier
728 label_this_week: cette semaine
729 label_this_week: cette semaine
729 label_last_week: la semaine dernière
730 label_last_week: la semaine dernière
730 label_last_n_weeks: "les %{count} dernières semaines"
731 label_last_n_weeks: "les %{count} dernières semaines"
731 label_last_n_days: "les %{count} derniers jours"
732 label_last_n_days: "les %{count} derniers jours"
732 label_this_month: ce mois-ci
733 label_this_month: ce mois-ci
733 label_last_month: le mois dernier
734 label_last_month: le mois dernier
734 label_this_year: cette annΓ©e
735 label_this_year: cette annΓ©e
735 label_date_range: PΓ©riode
736 label_date_range: PΓ©riode
736 label_less_than_ago: il y a moins de
737 label_less_than_ago: il y a moins de
737 label_more_than_ago: il y a plus de
738 label_more_than_ago: il y a plus de
738 label_ago: il y a
739 label_ago: il y a
739 label_contains: contient
740 label_contains: contient
740 label_not_contains: ne contient pas
741 label_not_contains: ne contient pas
741 label_any_issues_in_project: une demande du projet
742 label_any_issues_in_project: une demande du projet
742 label_any_issues_not_in_project: une demande hors du projet
743 label_any_issues_not_in_project: une demande hors du projet
743 label_no_issues_in_project: aucune demande du projet
744 label_no_issues_in_project: aucune demande du projet
744 label_any_open_issues: une demande ouverte
745 label_any_open_issues: une demande ouverte
745 label_no_open_issues: aucune demande ouverte
746 label_no_open_issues: aucune demande ouverte
746 label_day_plural: jours
747 label_day_plural: jours
747 label_repository: DΓ©pΓ΄t
748 label_repository: DΓ©pΓ΄t
748 label_repository_new: Nouveau dΓ©pΓ΄t
749 label_repository_new: Nouveau dΓ©pΓ΄t
749 label_repository_plural: DΓ©pΓ΄ts
750 label_repository_plural: DΓ©pΓ΄ts
750 label_browse: Parcourir
751 label_browse: Parcourir
751 label_branch: Branche
752 label_branch: Branche
752 label_tag: Tag
753 label_tag: Tag
753 label_revision: RΓ©vision
754 label_revision: RΓ©vision
754 label_revision_plural: RΓ©visions
755 label_revision_plural: RΓ©visions
755 label_revision_id: "RΓ©vision %{value}"
756 label_revision_id: "RΓ©vision %{value}"
756 label_associated_revisions: RΓ©visions associΓ©es
757 label_associated_revisions: RΓ©visions associΓ©es
757 label_added: ajoutΓ©
758 label_added: ajoutΓ©
758 label_modified: modifiΓ©
759 label_modified: modifiΓ©
759 label_copied: copiΓ©
760 label_copied: copiΓ©
760 label_renamed: renommΓ©
761 label_renamed: renommΓ©
761 label_deleted: supprimΓ©
762 label_deleted: supprimΓ©
762 label_latest_revision: Dernière révision
763 label_latest_revision: Dernière révision
763 label_latest_revision_plural: Dernières révisions
764 label_latest_revision_plural: Dernières révisions
764 label_view_revisions: Voir les rΓ©visions
765 label_view_revisions: Voir les rΓ©visions
765 label_view_all_revisions: Voir toutes les rΓ©visions
766 label_view_all_revisions: Voir toutes les rΓ©visions
766 label_max_size: Taille maximale
767 label_max_size: Taille maximale
767 label_sort_highest: Remonter en premier
768 label_sort_highest: Remonter en premier
768 label_sort_higher: Remonter
769 label_sort_higher: Remonter
769 label_sort_lower: Descendre
770 label_sort_lower: Descendre
770 label_sort_lowest: Descendre en dernier
771 label_sort_lowest: Descendre en dernier
771 label_roadmap: Roadmap
772 label_roadmap: Roadmap
772 label_roadmap_due_in: "Γ‰chΓ©ance dans %{value}"
773 label_roadmap_due_in: "Γ‰chΓ©ance dans %{value}"
773 label_roadmap_overdue: "En retard de %{value}"
774 label_roadmap_overdue: "En retard de %{value}"
774 label_roadmap_no_issues: Aucune demande pour cette version
775 label_roadmap_no_issues: Aucune demande pour cette version
775 label_search: Recherche
776 label_search: Recherche
776 label_result_plural: RΓ©sultats
777 label_result_plural: RΓ©sultats
777 label_all_words: Tous les mots
778 label_all_words: Tous les mots
778 label_wiki: Wiki
779 label_wiki: Wiki
779 label_wiki_edit: RΓ©vision wiki
780 label_wiki_edit: RΓ©vision wiki
780 label_wiki_edit_plural: RΓ©visions wiki
781 label_wiki_edit_plural: RΓ©visions wiki
781 label_wiki_page: Page wiki
782 label_wiki_page: Page wiki
782 label_wiki_page_plural: Pages wiki
783 label_wiki_page_plural: Pages wiki
783 label_wiki_page_new: Nouvelle page wiki
784 label_wiki_page_new: Nouvelle page wiki
784 label_index_by_title: Index par titre
785 label_index_by_title: Index par titre
785 label_index_by_date: Index par date
786 label_index_by_date: Index par date
786 label_current_version: Version actuelle
787 label_current_version: Version actuelle
787 label_preview: PrΓ©visualisation
788 label_preview: PrΓ©visualisation
788 label_feed_plural: Flux Atom
789 label_feed_plural: Flux Atom
789 label_changes_details: DΓ©tails de tous les changements
790 label_changes_details: DΓ©tails de tous les changements
790 label_issue_tracking: Suivi des demandes
791 label_issue_tracking: Suivi des demandes
791 label_spent_time: Temps passΓ©
792 label_spent_time: Temps passΓ©
792 label_total_spent_time: Temps passΓ© total
793 label_total_spent_time: Temps passΓ© total
793 label_overall_spent_time: Temps passΓ© global
794 label_overall_spent_time: Temps passΓ© global
794 label_f_hour: "%{value} heure"
795 label_f_hour: "%{value} heure"
795 label_f_hour_plural: "%{value} heures"
796 label_f_hour_plural: "%{value} heures"
796 label_f_hour_short: "%{value} h"
797 label_f_hour_short: "%{value} h"
797 label_time_tracking: Suivi du temps
798 label_time_tracking: Suivi du temps
798 label_change_plural: Changements
799 label_change_plural: Changements
799 label_statistics: Statistiques
800 label_statistics: Statistiques
800 label_commits_per_month: Commits par mois
801 label_commits_per_month: Commits par mois
801 label_commits_per_author: Commits par auteur
802 label_commits_per_author: Commits par auteur
802 label_diff: diff
803 label_diff: diff
803 label_view_diff: Voir les diffΓ©rences
804 label_view_diff: Voir les diffΓ©rences
804 label_diff_inline: en ligne
805 label_diff_inline: en ligne
805 label_diff_side_by_side: cΓ΄te Γ  cΓ΄te
806 label_diff_side_by_side: cΓ΄te Γ  cΓ΄te
806 label_options: Options
807 label_options: Options
807 label_copy_workflow_from: Copier le workflow de
808 label_copy_workflow_from: Copier le workflow de
808 label_permissions_report: Synthèse des permissions
809 label_permissions_report: Synthèse des permissions
809 label_watched_issues: Demandes surveillΓ©es
810 label_watched_issues: Demandes surveillΓ©es
810 label_related_issues: Demandes liΓ©es
811 label_related_issues: Demandes liΓ©es
811 label_applied_status: Statut appliquΓ©
812 label_applied_status: Statut appliquΓ©
812 label_loading: Chargement...
813 label_loading: Chargement...
813 label_relation_new: Nouvelle relation
814 label_relation_new: Nouvelle relation
814 label_relation_delete: Supprimer la relation
815 label_relation_delete: Supprimer la relation
815 label_relates_to: LiΓ© Γ 
816 label_relates_to: LiΓ© Γ 
816 label_duplicates: Duplique
817 label_duplicates: Duplique
817 label_duplicated_by: DupliquΓ© par
818 label_duplicated_by: DupliquΓ© par
818 label_blocks: Bloque
819 label_blocks: Bloque
819 label_blocked_by: BloquΓ© par
820 label_blocked_by: BloquΓ© par
820 label_precedes: Précède
821 label_precedes: Précède
821 label_follows: Suit
822 label_follows: Suit
822 label_copied_to: CopiΓ© vers
823 label_copied_to: CopiΓ© vers
823 label_copied_from: CopiΓ© depuis
824 label_copied_from: CopiΓ© depuis
824 label_stay_logged_in: Rester connectΓ©
825 label_stay_logged_in: Rester connectΓ©
825 label_disabled: dΓ©sactivΓ©
826 label_disabled: dΓ©sactivΓ©
826 label_show_completed_versions: Voir les versions passΓ©es
827 label_show_completed_versions: Voir les versions passΓ©es
827 label_me: moi
828 label_me: moi
828 label_board: Forum
829 label_board: Forum
829 label_board_new: Nouveau forum
830 label_board_new: Nouveau forum
830 label_board_plural: Forums
831 label_board_plural: Forums
831 label_board_locked: VerrouillΓ©
832 label_board_locked: VerrouillΓ©
832 label_board_sticky: Sticky
833 label_board_sticky: Sticky
833 label_topic_plural: Discussions
834 label_topic_plural: Discussions
834 label_message_plural: Messages
835 label_message_plural: Messages
835 label_message_last: Dernier message
836 label_message_last: Dernier message
836 label_message_new: Nouveau message
837 label_message_new: Nouveau message
837 label_message_posted: Message ajoutΓ©
838 label_message_posted: Message ajoutΓ©
838 label_reply_plural: RΓ©ponses
839 label_reply_plural: RΓ©ponses
839 label_send_information: Envoyer les informations Γ  l'utilisateur
840 label_send_information: Envoyer les informations Γ  l'utilisateur
840 label_year: AnnΓ©e
841 label_year: AnnΓ©e
841 label_month: Mois
842 label_month: Mois
842 label_week: Semaine
843 label_week: Semaine
843 label_date_from: Du
844 label_date_from: Du
844 label_date_to: Au
845 label_date_to: Au
845 label_language_based: BasΓ© sur la langue de l'utilisateur
846 label_language_based: BasΓ© sur la langue de l'utilisateur
846 label_sort_by: "Trier par %{value}"
847 label_sort_by: "Trier par %{value}"
847 label_send_test_email: Envoyer un email de test
848 label_send_test_email: Envoyer un email de test
848 label_feeds_access_key: Clé d'accès Atom
849 label_feeds_access_key: Clé d'accès Atom
849 label_missing_feeds_access_key: Clé d'accès Atom manquante
850 label_missing_feeds_access_key: Clé d'accès Atom manquante
850 label_feeds_access_key_created_on: "Clé d'accès Atom créée il y a %{value}"
851 label_feeds_access_key_created_on: "Clé d'accès Atom créée il y a %{value}"
851 label_module_plural: Modules
852 label_module_plural: Modules
852 label_added_time_by: "AjoutΓ© par %{author} il y a %{age}"
853 label_added_time_by: "AjoutΓ© par %{author} il y a %{age}"
853 label_updated_time_by: "Mis Γ  jour par %{author} il y a %{age}"
854 label_updated_time_by: "Mis Γ  jour par %{author} il y a %{age}"
854 label_updated_time: "Mis Γ  jour il y a %{value}"
855 label_updated_time: "Mis Γ  jour il y a %{value}"
855 label_jump_to_a_project: Aller Γ  un projet...
856 label_jump_to_a_project: Aller Γ  un projet...
856 label_file_plural: Fichiers
857 label_file_plural: Fichiers
857 label_changeset_plural: RΓ©visions
858 label_changeset_plural: RΓ©visions
858 label_default_columns: Colonnes par dΓ©faut
859 label_default_columns: Colonnes par dΓ©faut
859 label_no_change_option: (Pas de changement)
860 label_no_change_option: (Pas de changement)
860 label_bulk_edit_selected_issues: Modifier les demandes sΓ©lectionnΓ©es
861 label_bulk_edit_selected_issues: Modifier les demandes sΓ©lectionnΓ©es
861 label_bulk_edit_selected_time_entries: Modifier les temps passΓ©s sΓ©lectionnΓ©s
862 label_bulk_edit_selected_time_entries: Modifier les temps passΓ©s sΓ©lectionnΓ©s
862 label_theme: Thème
863 label_theme: Thème
863 label_default: DΓ©faut
864 label_default: DΓ©faut
864 label_search_titles_only: Uniquement dans les titres
865 label_search_titles_only: Uniquement dans les titres
865 label_user_mail_option_all: "Pour tous les Γ©vΓ©nements de tous mes projets"
866 label_user_mail_option_all: "Pour tous les Γ©vΓ©nements de tous mes projets"
866 label_user_mail_option_selected: "Pour tous les Γ©vΓ©nements des projets sΓ©lectionnΓ©s..."
867 label_user_mail_option_selected: "Pour tous les Γ©vΓ©nements des projets sΓ©lectionnΓ©s..."
867 label_user_mail_option_none: Aucune notification
868 label_user_mail_option_none: Aucune notification
868 label_user_mail_option_only_my_events: Seulement pour ce que je surveille
869 label_user_mail_option_only_my_events: Seulement pour ce que je surveille
869 label_user_mail_option_only_assigned: Seulement pour ce qui m'est assignΓ©
870 label_user_mail_option_only_assigned: Seulement pour ce qui m'est assignΓ©
870 label_user_mail_option_only_owner: Seulement pour ce que j'ai créé
871 label_user_mail_option_only_owner: Seulement pour ce que j'ai créé
871 label_user_mail_no_self_notified: "Je ne veux pas Γͺtre notifiΓ© des changements que j'effectue"
872 label_user_mail_no_self_notified: "Je ne veux pas Γͺtre notifiΓ© des changements que j'effectue"
872 label_registration_activation_by_email: activation du compte par email
873 label_registration_activation_by_email: activation du compte par email
873 label_registration_manual_activation: activation manuelle du compte
874 label_registration_manual_activation: activation manuelle du compte
874 label_registration_automatic_activation: activation automatique du compte
875 label_registration_automatic_activation: activation automatique du compte
875 label_display_per_page: "Par page : %{value}"
876 label_display_per_page: "Par page : %{value}"
876 label_age: Γ‚ge
877 label_age: Γ‚ge
877 label_change_properties: Changer les propriΓ©tΓ©s
878 label_change_properties: Changer les propriΓ©tΓ©s
878 label_general: GΓ©nΓ©ral
879 label_general: GΓ©nΓ©ral
879 label_more: Plus
880 label_more: Plus
880 label_scm: SCM
881 label_scm: SCM
881 label_plugins: Plugins
882 label_plugins: Plugins
882 label_ldap_authentication: Authentification LDAP
883 label_ldap_authentication: Authentification LDAP
883 label_downloads_abbr: D/L
884 label_downloads_abbr: D/L
884 label_optional_description: Description facultative
885 label_optional_description: Description facultative
885 label_add_another_file: Ajouter un autre fichier
886 label_add_another_file: Ajouter un autre fichier
886 label_preferences: PrΓ©fΓ©rences
887 label_preferences: PrΓ©fΓ©rences
887 label_chronological_order: Dans l'ordre chronologique
888 label_chronological_order: Dans l'ordre chronologique
888 label_reverse_chronological_order: Dans l'ordre chronologique inverse
889 label_reverse_chronological_order: Dans l'ordre chronologique inverse
889 label_planning: Planning
890 label_planning: Planning
890 label_incoming_emails: Emails entrants
891 label_incoming_emails: Emails entrants
891 label_generate_key: GΓ©nΓ©rer une clΓ©
892 label_generate_key: GΓ©nΓ©rer une clΓ©
892 label_issue_watchers: Observateurs
893 label_issue_watchers: Observateurs
893 label_example: Exemple
894 label_example: Exemple
894 label_display: Affichage
895 label_display: Affichage
895 label_sort: Tri
896 label_sort: Tri
896 label_ascending: Croissant
897 label_ascending: Croissant
897 label_descending: DΓ©croissant
898 label_descending: DΓ©croissant
898 label_date_from_to: Du %{start} au %{end}
899 label_date_from_to: Du %{start} au %{end}
899 label_wiki_content_added: Page wiki ajoutΓ©e
900 label_wiki_content_added: Page wiki ajoutΓ©e
900 label_wiki_content_updated: Page wiki mise Γ  jour
901 label_wiki_content_updated: Page wiki mise Γ  jour
901 label_group: Groupe
902 label_group: Groupe
902 label_group_plural: Groupes
903 label_group_plural: Groupes
903 label_group_new: Nouveau groupe
904 label_group_new: Nouveau groupe
904 label_group_anonymous: Utilisateurs anonymes
905 label_group_anonymous: Utilisateurs anonymes
905 label_group_non_member: Utilisateurs non membres
906 label_group_non_member: Utilisateurs non membres
906 label_time_entry_plural: Temps passΓ©
907 label_time_entry_plural: Temps passΓ©
907 label_version_sharing_none: Non partagΓ©
908 label_version_sharing_none: Non partagΓ©
908 label_version_sharing_descendants: Avec les sous-projets
909 label_version_sharing_descendants: Avec les sous-projets
909 label_version_sharing_hierarchy: Avec toute la hiΓ©rarchie
910 label_version_sharing_hierarchy: Avec toute la hiΓ©rarchie
910 label_version_sharing_tree: Avec tout l'arbre
911 label_version_sharing_tree: Avec tout l'arbre
911 label_version_sharing_system: Avec tous les projets
912 label_version_sharing_system: Avec tous les projets
912 label_update_issue_done_ratios: Mettre Γ  jour l'avancement des demandes
913 label_update_issue_done_ratios: Mettre Γ  jour l'avancement des demandes
913 label_copy_source: Source
914 label_copy_source: Source
914 label_copy_target: Cible
915 label_copy_target: Cible
915 label_copy_same_as_target: Comme la cible
916 label_copy_same_as_target: Comme la cible
916 label_display_used_statuses_only: N'afficher que les statuts utilisΓ©s dans ce tracker
917 label_display_used_statuses_only: N'afficher que les statuts utilisΓ©s dans ce tracker
917 label_api_access_key: Clé d'accès API
918 label_api_access_key: Clé d'accès API
918 label_missing_api_access_key: Clé d'accès API manquante
919 label_missing_api_access_key: Clé d'accès API manquante
919 label_api_access_key_created_on: Clé d'accès API créée il y a %{value}
920 label_api_access_key_created_on: Clé d'accès API créée il y a %{value}
920 label_profile: Profil
921 label_profile: Profil
921 label_subtask_plural: Sous-tΓ’ches
922 label_subtask_plural: Sous-tΓ’ches
922 label_project_copy_notifications: Envoyer les notifications durant la copie du projet
923 label_project_copy_notifications: Envoyer les notifications durant la copie du projet
923 label_principal_search: "Rechercher un utilisateur ou un groupe :"
924 label_principal_search: "Rechercher un utilisateur ou un groupe :"
924 label_user_search: "Rechercher un utilisateur :"
925 label_user_search: "Rechercher un utilisateur :"
925 label_additional_workflow_transitions_for_author: Autorisations supplémentaires lorsque l'utilisateur a créé la demande
926 label_additional_workflow_transitions_for_author: Autorisations supplémentaires lorsque l'utilisateur a créé la demande
926 label_additional_workflow_transitions_for_assignee: Autorisations supplΓ©mentaires lorsque la demande est assignΓ©e Γ  l'utilisateur
927 label_additional_workflow_transitions_for_assignee: Autorisations supplΓ©mentaires lorsque la demande est assignΓ©e Γ  l'utilisateur
927 label_issues_visibility_all: Toutes les demandes
928 label_issues_visibility_all: Toutes les demandes
928 label_issues_visibility_public: Toutes les demandes non privΓ©es
929 label_issues_visibility_public: Toutes les demandes non privΓ©es
929 label_issues_visibility_own: Demandes créées par ou assignées à l'utilisateur
930 label_issues_visibility_own: Demandes créées par ou assignées à l'utilisateur
930 label_git_report_last_commit: Afficher le dernier commit des fichiers et rΓ©pertoires
931 label_git_report_last_commit: Afficher le dernier commit des fichiers et rΓ©pertoires
931 label_parent_revision: Parent
932 label_parent_revision: Parent
932 label_child_revision: Enfant
933 label_child_revision: Enfant
933 label_export_options: Options d'exportation %{export_format}
934 label_export_options: Options d'exportation %{export_format}
934 label_copy_attachments: Copier les fichiers
935 label_copy_attachments: Copier les fichiers
935 label_copy_subtasks: Copier les sous-tΓ’ches
936 label_copy_subtasks: Copier les sous-tΓ’ches
936 label_item_position: "%{position} sur %{count}"
937 label_item_position: "%{position} sur %{count}"
937 label_completed_versions: Versions passΓ©es
938 label_completed_versions: Versions passΓ©es
938 label_search_for_watchers: Rechercher des observateurs
939 label_search_for_watchers: Rechercher des observateurs
939 label_session_expiration: Expiration des sessions
940 label_session_expiration: Expiration des sessions
940 label_show_closed_projects: Voir les projets fermΓ©s
941 label_show_closed_projects: Voir les projets fermΓ©s
941 label_status_transitions: Changements de statut
942 label_status_transitions: Changements de statut
942 label_fields_permissions: Permissions sur les champs
943 label_fields_permissions: Permissions sur les champs
943 label_readonly: Lecture
944 label_readonly: Lecture
944 label_required: Obligatoire
945 label_required: Obligatoire
945 label_hidden: CachΓ©
946 label_hidden: CachΓ©
946 label_attribute_of_project: "%{name} du projet"
947 label_attribute_of_project: "%{name} du projet"
947 label_attribute_of_issue: "%{name} de la demande"
948 label_attribute_of_issue: "%{name} de la demande"
948 label_attribute_of_author: "%{name} de l'auteur"
949 label_attribute_of_author: "%{name} de l'auteur"
949 label_attribute_of_assigned_to: "%{name} de l'assignΓ©"
950 label_attribute_of_assigned_to: "%{name} de l'assignΓ©"
950 label_attribute_of_user: "%{name} de l'utilisateur"
951 label_attribute_of_user: "%{name} de l'utilisateur"
951 label_attribute_of_fixed_version: "%{name} de la version cible"
952 label_attribute_of_fixed_version: "%{name} de la version cible"
952 label_cross_project_descendants: Avec les sous-projets
953 label_cross_project_descendants: Avec les sous-projets
953 label_cross_project_tree: Avec tout l'arbre
954 label_cross_project_tree: Avec tout l'arbre
954 label_cross_project_hierarchy: Avec toute la hiΓ©rarchie
955 label_cross_project_hierarchy: Avec toute la hiΓ©rarchie
955 label_cross_project_system: Avec tous les projets
956 label_cross_project_system: Avec tous les projets
956 label_gantt_progress_line: Ligne de progression
957 label_gantt_progress_line: Ligne de progression
957 label_visibility_private: par moi uniquement
958 label_visibility_private: par moi uniquement
958 label_visibility_roles: par ces rΓ΄les uniquement
959 label_visibility_roles: par ces rΓ΄les uniquement
959 label_visibility_public: par tout le monde
960 label_visibility_public: par tout le monde
960 label_link: Lien
961 label_link: Lien
961 label_only: seulement
962 label_only: seulement
962 label_drop_down_list: liste dΓ©roulante
963 label_drop_down_list: liste dΓ©roulante
963 label_checkboxes: cases Γ  cocher
964 label_checkboxes: cases Γ  cocher
964 label_radio_buttons: boutons radio
965 label_radio_buttons: boutons radio
965 label_link_values_to: Lier les valeurs vers l'URL
966 label_link_values_to: Lier les valeurs vers l'URL
966 label_custom_field_select_type: Selectionner le type d'objet auquel attacher le champ personnalisΓ©
967 label_custom_field_select_type: Selectionner le type d'objet auquel attacher le champ personnalisΓ©
967 label_check_for_updates: VΓ©rifier les mises Γ  jour
968 label_check_for_updates: VΓ©rifier les mises Γ  jour
968 label_latest_compatible_version: Dernière version compatible
969 label_latest_compatible_version: Dernière version compatible
969 label_unknown_plugin: Plugin inconnu
970 label_unknown_plugin: Plugin inconnu
970 label_add_projects: Ajouter des projets
971 label_add_projects: Ajouter des projets
971 label_users_visibility_all: Tous les utilisateurs actifs
972 label_users_visibility_all: Tous les utilisateurs actifs
972 label_users_visibility_members_of_visible_projects: Membres des projets visibles
973 label_users_visibility_members_of_visible_projects: Membres des projets visibles
973 label_edit_attachments: Modifier les fichiers attachΓ©s
974 label_edit_attachments: Modifier les fichiers attachΓ©s
974 label_link_copied_issue: Lier la demande copiΓ©e
975 label_link_copied_issue: Lier la demande copiΓ©e
975 label_ask: Demander
976 label_ask: Demander
976 label_search_attachments_yes: Rechercher les noms et descriptions de fichiers
977 label_search_attachments_yes: Rechercher les noms et descriptions de fichiers
977 label_search_attachments_no: Ne pas rechercher les fichiers
978 label_search_attachments_no: Ne pas rechercher les fichiers
978 label_search_attachments_only: Rechercher les fichiers uniquement
979 label_search_attachments_only: Rechercher les fichiers uniquement
979 label_search_open_issues_only: Demandes ouvertes uniquement
980 label_search_open_issues_only: Demandes ouvertes uniquement
980 label_email_address_plural: Emails
981 label_email_address_plural: Emails
981 label_email_address_add: Ajouter une adresse email
982 label_email_address_add: Ajouter une adresse email
982 label_enable_notifications: Activer les notifications
983 label_enable_notifications: Activer les notifications
983 label_disable_notifications: DΓ©sactiver les notifications
984 label_disable_notifications: DΓ©sactiver les notifications
984 label_blank_value: non renseignΓ©
985 label_blank_value: non renseignΓ©
985 label_parent_task_attributes: Attributs des tΓ’ches parentes
986 label_parent_task_attributes: Attributs des tΓ’ches parentes
986 label_time_entries_visibility_all: Tous les temps passΓ©s
987 label_time_entries_visibility_all: Tous les temps passΓ©s
987 label_time_entries_visibility_own: Ses propres temps passΓ©s
988 label_time_entries_visibility_own: Ses propres temps passΓ©s
988 label_member_management: Gestion des membres
989 label_member_management: Gestion des membres
989 label_member_management_all_roles: Tous les rΓ΄les
990 label_member_management_all_roles: Tous les rΓ΄les
990 label_member_management_selected_roles_only: Ces rΓ΄les uniquement
991 label_member_management_selected_roles_only: Ces rΓ΄les uniquement
991 label_import_issues: Importer des demandes
992 label_import_issues: Importer des demandes
992 label_select_file_to_import: SΓ©lectionner le fichier Γ  importer
993 label_select_file_to_import: SΓ©lectionner le fichier Γ  importer
993 label_fields_separator: SΓ©parateur de champs
994 label_fields_separator: SΓ©parateur de champs
994 label_fields_wrapper: DΓ©limiteur de texte
995 label_fields_wrapper: DΓ©limiteur de texte
995 label_encoding: Encodage
996 label_encoding: Encodage
996 label_comma_char: Virgule
997 label_comma_char: Virgule
997 label_semi_colon_char: Point virgule
998 label_semi_colon_char: Point virgule
998 label_quote_char: Apostrophe
999 label_quote_char: Apostrophe
999 label_double_quote_char: Double apostrophe
1000 label_double_quote_char: Double apostrophe
1000 label_fields_mapping: Correspondance des champs
1001 label_fields_mapping: Correspondance des champs
1001 label_file_content_preview: AperΓ§u du contenu du fichier
1002 label_file_content_preview: AperΓ§u du contenu du fichier
1002 label_create_missing_values: CrΓ©er les valeurs manquantes
1003 label_create_missing_values: CrΓ©er les valeurs manquantes
1003 label_api: API
1004 label_api: API
1004 label_field_format_enumeration: Liste clΓ©/valeur
1005 label_field_format_enumeration: Liste clΓ©/valeur
1005 label_default_values_for_new_users: Valeurs par dΓ©faut pour les nouveaux utilisateurs
1006 label_default_values_for_new_users: Valeurs par dΓ©faut pour les nouveaux utilisateurs
1006 label_relations: Relations
1007 label_relations: Relations
1007 label_new_project_issue_tab_enabled: Afficher l'onglet "Nouvelle demande"
1008 label_new_project_issue_tab_enabled: Afficher l'onglet "Nouvelle demande"
1008 label_new_object_tab_enabled: Afficher le menu dΓ©roulant "+"
1009 label_new_object_tab_enabled: Afficher le menu dΓ©roulant "+"
1009
1010
1010 button_login: Connexion
1011 button_login: Connexion
1011 button_submit: Soumettre
1012 button_submit: Soumettre
1012 button_save: Sauvegarder
1013 button_save: Sauvegarder
1013 button_check_all: Tout cocher
1014 button_check_all: Tout cocher
1014 button_uncheck_all: Tout dΓ©cocher
1015 button_uncheck_all: Tout dΓ©cocher
1015 button_collapse_all: Plier tout
1016 button_collapse_all: Plier tout
1016 button_expand_all: DΓ©plier tout
1017 button_expand_all: DΓ©plier tout
1017 button_delete: Supprimer
1018 button_delete: Supprimer
1018 button_create: CrΓ©er
1019 button_create: CrΓ©er
1019 button_create_and_continue: CrΓ©er et continuer
1020 button_create_and_continue: CrΓ©er et continuer
1020 button_test: Tester
1021 button_test: Tester
1021 button_edit: Modifier
1022 button_edit: Modifier
1022 button_edit_associated_wikipage: "Modifier la page wiki associΓ©e: %{page_title}"
1023 button_edit_associated_wikipage: "Modifier la page wiki associΓ©e: %{page_title}"
1023 button_add: Ajouter
1024 button_add: Ajouter
1024 button_change: Changer
1025 button_change: Changer
1025 button_apply: Appliquer
1026 button_apply: Appliquer
1026 button_clear: Effacer
1027 button_clear: Effacer
1027 button_lock: Verrouiller
1028 button_lock: Verrouiller
1028 button_unlock: DΓ©verrouiller
1029 button_unlock: DΓ©verrouiller
1029 button_download: TΓ©lΓ©charger
1030 button_download: TΓ©lΓ©charger
1030 button_list: Lister
1031 button_list: Lister
1031 button_view: Voir
1032 button_view: Voir
1032 button_move: DΓ©placer
1033 button_move: DΓ©placer
1033 button_move_and_follow: DΓ©placer et suivre
1034 button_move_and_follow: DΓ©placer et suivre
1034 button_back: Retour
1035 button_back: Retour
1035 button_cancel: Annuler
1036 button_cancel: Annuler
1036 button_activate: Activer
1037 button_activate: Activer
1037 button_sort: Trier
1038 button_sort: Trier
1038 button_log_time: Saisir temps
1039 button_log_time: Saisir temps
1039 button_rollback: Revenir Γ  cette version
1040 button_rollback: Revenir Γ  cette version
1040 button_watch: Surveiller
1041 button_watch: Surveiller
1041 button_unwatch: Ne plus surveiller
1042 button_unwatch: Ne plus surveiller
1042 button_reply: RΓ©pondre
1043 button_reply: RΓ©pondre
1043 button_archive: Archiver
1044 button_archive: Archiver
1044 button_unarchive: DΓ©sarchiver
1045 button_unarchive: DΓ©sarchiver
1045 button_reset: RΓ©initialiser
1046 button_reset: RΓ©initialiser
1046 button_rename: Renommer
1047 button_rename: Renommer
1047 button_change_password: Changer de mot de passe
1048 button_change_password: Changer de mot de passe
1048 button_copy: Copier
1049 button_copy: Copier
1049 button_copy_and_follow: Copier et suivre
1050 button_copy_and_follow: Copier et suivre
1050 button_annotate: Annoter
1051 button_annotate: Annoter
1051 button_update: Mettre Γ  jour
1052 button_update: Mettre Γ  jour
1052 button_configure: Configurer
1053 button_configure: Configurer
1053 button_quote: Citer
1054 button_quote: Citer
1054 button_duplicate: Dupliquer
1055 button_duplicate: Dupliquer
1055 button_show: Afficher
1056 button_show: Afficher
1056 button_hide: Cacher
1057 button_hide: Cacher
1057 button_edit_section: Modifier cette section
1058 button_edit_section: Modifier cette section
1058 button_export: Exporter
1059 button_export: Exporter
1059 button_delete_my_account: Supprimer mon compte
1060 button_delete_my_account: Supprimer mon compte
1060 button_close: Fermer
1061 button_close: Fermer
1061 button_reopen: RΓ©ouvrir
1062 button_reopen: RΓ©ouvrir
1062 button_import: Importer
1063 button_import: Importer
1063 button_filter: Filtrer
1064 button_filter: Filtrer
1064
1065
1065 status_active: actif
1066 status_active: actif
1066 status_registered: enregistrΓ©
1067 status_registered: enregistrΓ©
1067 status_locked: verrouillΓ©
1068 status_locked: verrouillΓ©
1068
1069
1069 project_status_active: actif
1070 project_status_active: actif
1070 project_status_closed: fermΓ©
1071 project_status_closed: fermΓ©
1071 project_status_archived: archivΓ©
1072 project_status_archived: archivΓ©
1072
1073
1073 version_status_open: ouvert
1074 version_status_open: ouvert
1074 version_status_locked: verrouillΓ©
1075 version_status_locked: verrouillΓ©
1075 version_status_closed: fermΓ©
1076 version_status_closed: fermΓ©
1076
1077
1077 field_active: Actif
1078 field_active: Actif
1078
1079
1079 text_select_mail_notifications: Actions pour lesquelles une notification par e-mail est envoyΓ©e
1080 text_select_mail_notifications: Actions pour lesquelles une notification par e-mail est envoyΓ©e
1080 text_regexp_info: ex. ^[A-Z0-9]+$
1081 text_regexp_info: ex. ^[A-Z0-9]+$
1081 text_min_max_length_info: 0 pour aucune restriction
1082 text_min_max_length_info: 0 pour aucune restriction
1082 text_project_destroy_confirmation: Êtes-vous sûr de vouloir supprimer ce projet et toutes ses données ?
1083 text_project_destroy_confirmation: Êtes-vous sûr de vouloir supprimer ce projet et toutes ses données ?
1083 text_subprojects_destroy_warning: "Ses sous-projets : %{value} seront Γ©galement supprimΓ©s."
1084 text_subprojects_destroy_warning: "Ses sous-projets : %{value} seront Γ©galement supprimΓ©s."
1084 text_workflow_edit: SΓ©lectionner un tracker et un rΓ΄le pour Γ©diter le workflow
1085 text_workflow_edit: SΓ©lectionner un tracker et un rΓ΄le pour Γ©diter le workflow
1085 text_are_you_sure: Êtes-vous sûr ?
1086 text_are_you_sure: Êtes-vous sûr ?
1086 text_journal_changed: "%{label} changΓ© de %{old} Γ  %{new}"
1087 text_journal_changed: "%{label} changΓ© de %{old} Γ  %{new}"
1087 text_journal_changed_no_detail: "%{label} mis Γ  jour"
1088 text_journal_changed_no_detail: "%{label} mis Γ  jour"
1088 text_journal_set_to: "%{label} mis Γ  %{value}"
1089 text_journal_set_to: "%{label} mis Γ  %{value}"
1089 text_journal_deleted: "%{label} %{old} supprimΓ©"
1090 text_journal_deleted: "%{label} %{old} supprimΓ©"
1090 text_journal_added: "%{label} %{value} ajoutΓ©"
1091 text_journal_added: "%{label} %{value} ajoutΓ©"
1091 text_tip_issue_begin_day: tΓ’che commenΓ§ant ce jour
1092 text_tip_issue_begin_day: tΓ’che commenΓ§ant ce jour
1092 text_tip_issue_end_day: tΓ’che finissant ce jour
1093 text_tip_issue_end_day: tΓ’che finissant ce jour
1093 text_tip_issue_begin_end_day: tΓ’che commenΓ§ant et finissant ce jour
1094 text_tip_issue_begin_end_day: tΓ’che commenΓ§ant et finissant ce jour
1094 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 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 text_caracters_maximum: "%{count} caractères maximum."
1096 text_caracters_maximum: "%{count} caractères maximum."
1096 text_caracters_minimum: "%{count} caractères minimum."
1097 text_caracters_minimum: "%{count} caractères minimum."
1097 text_length_between: "Longueur comprise entre %{min} et %{max} caractères."
1098 text_length_between: "Longueur comprise entre %{min} et %{max} caractères."
1098 text_tracker_no_workflow: Aucun worflow n'est dΓ©fini pour ce tracker
1099 text_tracker_no_workflow: Aucun worflow n'est dΓ©fini pour ce tracker
1099 text_unallowed_characters: Caractères non autorisés
1100 text_unallowed_characters: Caractères non autorisés
1100 text_comma_separated: Plusieurs valeurs possibles (sΓ©parΓ©es par des virgules).
1101 text_comma_separated: Plusieurs valeurs possibles (sΓ©parΓ©es par des virgules).
1101 text_line_separated: Plusieurs valeurs possibles (une valeur par ligne).
1102 text_line_separated: Plusieurs valeurs possibles (une valeur par ligne).
1102 text_issues_ref_in_commit_messages: RΓ©fΓ©rencement et rΓ©solution des demandes dans les commentaires de commits
1103 text_issues_ref_in_commit_messages: RΓ©fΓ©rencement et rΓ©solution des demandes dans les commentaires de commits
1103 text_issue_added: "La demande %{id} a Γ©tΓ© soumise par %{author}."
1104 text_issue_added: "La demande %{id} a Γ©tΓ© soumise par %{author}."
1104 text_issue_updated: "La demande %{id} a Γ©tΓ© mise Γ  jour par %{author}."
1105 text_issue_updated: "La demande %{id} a Γ©tΓ© mise Γ  jour par %{author}."
1105 text_wiki_destroy_confirmation: Etes-vous sΓ»r de vouloir supprimer ce wiki et tout son contenu ?
1106 text_wiki_destroy_confirmation: Etes-vous sΓ»r de vouloir supprimer ce wiki et tout son contenu ?
1106 text_issue_category_destroy_question: "%{count} demandes sont affectΓ©es Γ  cette catΓ©gorie. Que voulez-vous faire ?"
1107 text_issue_category_destroy_question: "%{count} demandes sont affectΓ©es Γ  cette catΓ©gorie. Que voulez-vous faire ?"
1107 text_issue_category_destroy_assignments: N'affecter les demandes Γ  aucune autre catΓ©gorie
1108 text_issue_category_destroy_assignments: N'affecter les demandes Γ  aucune autre catΓ©gorie
1108 text_issue_category_reassign_to: RΓ©affecter les demandes Γ  cette catΓ©gorie
1109 text_issue_category_reassign_to: RΓ©affecter les demandes Γ  cette catΓ©gorie
1109 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 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 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 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 text_load_default_configuration: Charger le paramΓ©trage par dΓ©faut
1112 text_load_default_configuration: Charger le paramΓ©trage par dΓ©faut
1112 text_status_changed_by_changeset: "AppliquΓ© par commit %{value}."
1113 text_status_changed_by_changeset: "AppliquΓ© par commit %{value}."
1113 text_time_logged_by_changeset: "AppliquΓ© par commit %{value}"
1114 text_time_logged_by_changeset: "AppliquΓ© par commit %{value}"
1114 text_issues_destroy_confirmation: 'Êtes-vous sûr de vouloir supprimer la ou les demandes(s) selectionnée(s) ?'
1115 text_issues_destroy_confirmation: 'Êtes-vous sûr de vouloir supprimer la ou les demandes(s) selectionnée(s) ?'
1115 text_issues_destroy_descendants_confirmation: "Cela entrainera Γ©galement la suppression de %{count} sous-tΓ’che(s)."
1116 text_issues_destroy_descendants_confirmation: "Cela entrainera Γ©galement la suppression de %{count} sous-tΓ’che(s)."
1116 text_time_entries_destroy_confirmation: "Etes-vous sΓ»r de vouloir supprimer les temps passΓ©s sΓ©lectionnΓ©s ?"
1117 text_time_entries_destroy_confirmation: "Etes-vous sΓ»r de vouloir supprimer les temps passΓ©s sΓ©lectionnΓ©s ?"
1117 text_select_project_modules: 'SΓ©lectionner les modules Γ  activer pour ce projet :'
1118 text_select_project_modules: 'SΓ©lectionner les modules Γ  activer pour ce projet :'
1118 text_default_administrator_account_changed: Compte administrateur par dΓ©faut changΓ©
1119 text_default_administrator_account_changed: Compte administrateur par dΓ©faut changΓ©
1119 text_file_repository_writable: RΓ©pertoire de stockage des fichiers accessible en Γ©criture
1120 text_file_repository_writable: RΓ©pertoire de stockage des fichiers accessible en Γ©criture
1120 text_plugin_assets_writable: RΓ©pertoire public des plugins accessible en Γ©criture
1121 text_plugin_assets_writable: RΓ©pertoire public des plugins accessible en Γ©criture
1121 text_rmagick_available: Bibliothèque RMagick présente (optionnelle)
1122 text_rmagick_available: Bibliothèque RMagick présente (optionnelle)
1122 text_convert_available: Binaire convert de ImageMagick prΓ©sent (optionel)
1123 text_convert_available: Binaire convert de ImageMagick prΓ©sent (optionel)
1123 text_destroy_time_entries_question: "%{hours} heures ont Γ©tΓ© enregistrΓ©es sur les demandes Γ  supprimer. Que voulez-vous faire ?"
1124 text_destroy_time_entries_question: "%{hours} heures ont Γ©tΓ© enregistrΓ©es sur les demandes Γ  supprimer. Que voulez-vous faire ?"
1124 text_destroy_time_entries: Supprimer les heures
1125 text_destroy_time_entries: Supprimer les heures
1125 text_assign_time_entries_to_project: Reporter les heures sur le projet
1126 text_assign_time_entries_to_project: Reporter les heures sur le projet
1126 text_reassign_time_entries: 'Reporter les heures sur cette demande:'
1127 text_reassign_time_entries: 'Reporter les heures sur cette demande:'
1127 text_user_wrote: "%{value} a Γ©crit :"
1128 text_user_wrote: "%{value} a Γ©crit :"
1128 text_enumeration_destroy_question: "La valeur Β« %{name} Β» est affectΓ©e Γ  %{count} objet(s)."
1129 text_enumeration_destroy_question: "La valeur Β« %{name} Β» est affectΓ©e Γ  %{count} objet(s)."
1129 text_enumeration_category_reassign_to: 'RΓ©affecter les objets Γ  cette valeur:'
1130 text_enumeration_category_reassign_to: 'RΓ©affecter les objets Γ  cette valeur:'
1130 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 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 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 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 text_diff_truncated: '... Ce diffΓ©rentiel a Γ©tΓ© tronquΓ© car il excΓ¨de la taille maximale pouvant Γͺtre affichΓ©e.'
1133 text_diff_truncated: '... Ce diffΓ©rentiel a Γ©tΓ© tronquΓ© car il excΓ¨de la taille maximale pouvant Γͺtre affichΓ©e.'
1133 text_custom_field_possible_values_info: 'Une ligne par valeur'
1134 text_custom_field_possible_values_info: 'Une ligne par valeur'
1134 text_wiki_page_destroy_question: "Cette page possède %{descendants} sous-page(s) et descendante(s). Que voulez-vous faire ?"
1135 text_wiki_page_destroy_question: "Cette page possède %{descendants} sous-page(s) et descendante(s). Que voulez-vous faire ?"
1135 text_wiki_page_nullify_children: "Conserver les sous-pages en tant que pages racines"
1136 text_wiki_page_nullify_children: "Conserver les sous-pages en tant que pages racines"
1136 text_wiki_page_destroy_children: "Supprimer les sous-pages et toutes leurs descedantes"
1137 text_wiki_page_destroy_children: "Supprimer les sous-pages et toutes leurs descedantes"
1137 text_wiki_page_reassign_children: "RΓ©affecter les sous-pages Γ  cette page"
1138 text_wiki_page_reassign_children: "RΓ©affecter les sous-pages Γ  cette page"
1138 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 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 text_zoom_in: Zoom avant
1140 text_zoom_in: Zoom avant
1140 text_zoom_out: Zoom arrière
1141 text_zoom_out: Zoom arrière
1141 text_warn_on_leaving_unsaved: "Cette page contient du texte non sauvegardΓ© qui sera perdu si vous quittez la page."
1142 text_warn_on_leaving_unsaved: "Cette page contient du texte non sauvegardΓ© qui sera perdu si vous quittez la page."
1142 text_scm_path_encoding_note: "DΓ©faut : UTF-8"
1143 text_scm_path_encoding_note: "DΓ©faut : UTF-8"
1143 text_subversion_repository_note: "Exemples (en fonction des protocoles supportΓ©s) : file:///, http://, https://, svn://, svn+[tunnelscheme]://"
1144 text_subversion_repository_note: "Exemples (en fonction des protocoles supportΓ©s) : file:///, http://, https://, svn://, svn+[tunnelscheme]://"
1144 text_git_repository_note: "Chemin vers un dΓ©pΓ΄t vide et local (exemples : /gitrepo, c:\\gitrepo)"
1145 text_git_repository_note: "Chemin vers un dΓ©pΓ΄t vide et local (exemples : /gitrepo, c:\\gitrepo)"
1145 text_mercurial_repository_note: "Chemin vers un dΓ©pΓ΄t local (exemples : /hgrepo, c:\\hgrepo)"
1146 text_mercurial_repository_note: "Chemin vers un dΓ©pΓ΄t local (exemples : /hgrepo, c:\\hgrepo)"
1146 text_scm_command: Commande
1147 text_scm_command: Commande
1147 text_scm_command_version: Version
1148 text_scm_command_version: Version
1148 text_scm_config: Vous pouvez configurer les commandes des SCM dans config/configuration.yml. Redémarrer l'application après modification.
1149 text_scm_config: Vous pouvez configurer les commandes des SCM dans config/configuration.yml. Redémarrer l'application après modification.
1149 text_scm_command_not_available: Ce SCM n'est pas disponible. Vérifier les paramètres dans la section administration.
1150 text_scm_command_not_available: Ce SCM n'est pas disponible. Vérifier les paramètres dans la section administration.
1150 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 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 text_issue_conflict_resolution_add_notes: "Ajouter mes notes et ignorer mes autres changements"
1152 text_issue_conflict_resolution_add_notes: "Ajouter mes notes et ignorer mes autres changements"
1152 text_issue_conflict_resolution_cancel: "Annuler ma mise Γ  jour et rΓ©afficher %{link}"
1153 text_issue_conflict_resolution_cancel: "Annuler ma mise Γ  jour et rΓ©afficher %{link}"
1153 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 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 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 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 text_project_closed: Ce projet est fermΓ© et accessible en lecture seule.
1156 text_project_closed: Ce projet est fermΓ© et accessible en lecture seule.
1156 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 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 default_role_manager: Manager
1159 default_role_manager: Manager
1159 default_role_developer: DΓ©veloppeur
1160 default_role_developer: DΓ©veloppeur
1160 default_role_reporter: Rapporteur
1161 default_role_reporter: Rapporteur
1161 default_tracker_bug: Anomalie
1162 default_tracker_bug: Anomalie
1162 default_tracker_feature: Evolution
1163 default_tracker_feature: Evolution
1163 default_tracker_support: Assistance
1164 default_tracker_support: Assistance
1164 default_issue_status_new: Nouveau
1165 default_issue_status_new: Nouveau
1165 default_issue_status_in_progress: En cours
1166 default_issue_status_in_progress: En cours
1166 default_issue_status_resolved: RΓ©solu
1167 default_issue_status_resolved: RΓ©solu
1167 default_issue_status_feedback: Commentaire
1168 default_issue_status_feedback: Commentaire
1168 default_issue_status_closed: FermΓ©
1169 default_issue_status_closed: FermΓ©
1169 default_issue_status_rejected: RejetΓ©
1170 default_issue_status_rejected: RejetΓ©
1170 default_doc_category_user: Documentation utilisateur
1171 default_doc_category_user: Documentation utilisateur
1171 default_doc_category_tech: Documentation technique
1172 default_doc_category_tech: Documentation technique
1172 default_priority_low: Bas
1173 default_priority_low: Bas
1173 default_priority_normal: Normal
1174 default_priority_normal: Normal
1174 default_priority_high: Haut
1175 default_priority_high: Haut
1175 default_priority_urgent: Urgent
1176 default_priority_urgent: Urgent
1176 default_priority_immediate: ImmΓ©diat
1177 default_priority_immediate: ImmΓ©diat
1177 default_activity_design: Conception
1178 default_activity_design: Conception
1178 default_activity_development: DΓ©veloppement
1179 default_activity_development: DΓ©veloppement
1179
1180
1180 enumeration_issue_priorities: PrioritΓ©s des demandes
1181 enumeration_issue_priorities: PrioritΓ©s des demandes
1181 enumeration_doc_categories: CatΓ©gories des documents
1182 enumeration_doc_categories: CatΓ©gories des documents
1182 enumeration_activities: ActivitΓ©s (suivi du temps)
1183 enumeration_activities: ActivitΓ©s (suivi du temps)
1183 enumeration_system_activity: Activité système
1184 enumeration_system_activity: Activité système
1184 description_filter: Filtre
1185 description_filter: Filtre
1185 description_search: Champ de recherche
1186 description_search: Champ de recherche
1186 description_choose_project: Projets
1187 description_choose_project: Projets
1187 description_project_scope: Périmètre de recherche
1188 description_project_scope: Périmètre de recherche
1188 description_notes: Notes
1189 description_notes: Notes
1189 description_message_content: Contenu du message
1190 description_message_content: Contenu du message
1190 description_query_sort_criteria_attribute: Critère de tri
1191 description_query_sort_criteria_attribute: Critère de tri
1191 description_query_sort_criteria_direction: Ordre de tri
1192 description_query_sort_criteria_direction: Ordre de tri
1192 description_user_mail_notification: Option de notification
1193 description_user_mail_notification: Option de notification
1193 description_available_columns: Colonnes disponibles
1194 description_available_columns: Colonnes disponibles
1194 description_selected_columns: Colonnes sΓ©lectionnΓ©es
1195 description_selected_columns: Colonnes sΓ©lectionnΓ©es
1195 description_all_columns: Toutes les colonnes
1196 description_all_columns: Toutes les colonnes
1196 description_issue_category_reassign: Choisir une catΓ©gorie
1197 description_issue_category_reassign: Choisir une catΓ©gorie
1197 description_wiki_subpages_reassign: Choisir une nouvelle page parent
1198 description_wiki_subpages_reassign: Choisir une nouvelle page parent
1198 description_date_range_list: Choisir une pΓ©riode prΓ©dΓ©finie
1199 description_date_range_list: Choisir une pΓ©riode prΓ©dΓ©finie
1199 description_date_range_interval: Choisir une pΓ©riode
1200 description_date_range_interval: Choisir une pΓ©riode
1200 description_date_from: Date de dΓ©but
1201 description_date_from: Date de dΓ©but
1201 description_date_to: Date de fin
1202 description_date_to: Date de fin
1202 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 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 label_parent_task_attributes_derived: CalculΓ© Γ  partir des sous-tΓ’ches
1204 label_parent_task_attributes_derived: CalculΓ© Γ  partir des sous-tΓ’ches
1204 label_parent_task_attributes_independent: IndΓ©pendent des sous-tΓ’ches
1205 label_parent_task_attributes_independent: IndΓ©pendent des sous-tΓ’ches
1205 mail_subject_security_notification: Notification de sΓ©curitΓ©
1206 mail_subject_security_notification: Notification de sΓ©curitΓ©
1206 mail_body_security_notification_change: ! '%{field} modifiΓ©(e).'
1207 mail_body_security_notification_change: ! '%{field} modifiΓ©(e).'
1207 mail_body_security_notification_change_to: ! '%{field} changΓ©(e) en %{value}.'
1208 mail_body_security_notification_change_to: ! '%{field} changΓ©(e) en %{value}.'
1208 mail_body_security_notification_add: ! '%{field} %{value} ajoutΓ©(e).'
1209 mail_body_security_notification_add: ! '%{field} %{value} ajoutΓ©(e).'
1209 mail_body_security_notification_remove: ! '%{field} %{value} supprimΓ©(e).'
1210 mail_body_security_notification_remove: ! '%{field} %{value} supprimΓ©(e).'
1210 mail_body_security_notification_notify_enabled: Les notifications ont Γ©tΓ© activΓ©es pour l'adresse %{value}
1211 mail_body_security_notification_notify_enabled: Les notifications ont Γ©tΓ© activΓ©es pour l'adresse %{value}
1211 mail_body_security_notification_notify_disabled: Les notifications ont Γ©tΓ© dΓ©sactivΓ©es pour l'adresse %{value}
1212 mail_body_security_notification_notify_disabled: Les notifications ont Γ©tΓ© dΓ©sactivΓ©es pour l'adresse %{value}
1212 field_remote_ip: Adresse IP
1213 field_remote_ip: Adresse IP
1213 label_no_preview: No preview available
1214 label_no_preview: No preview available
@@ -1,4682 +1,4700
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class IssuesControllerTest < Redmine::ControllerTest
20 class IssuesControllerTest < Redmine::ControllerTest
21 fixtures :projects,
21 fixtures :projects,
22 :users, :email_addresses, :user_preferences,
22 :users, :email_addresses, :user_preferences,
23 :roles,
23 :roles,
24 :members,
24 :members,
25 :member_roles,
25 :member_roles,
26 :issues,
26 :issues,
27 :issue_statuses,
27 :issue_statuses,
28 :issue_relations,
28 :issue_relations,
29 :versions,
29 :versions,
30 :trackers,
30 :trackers,
31 :projects_trackers,
31 :projects_trackers,
32 :issue_categories,
32 :issue_categories,
33 :enabled_modules,
33 :enabled_modules,
34 :enumerations,
34 :enumerations,
35 :attachments,
35 :attachments,
36 :workflows,
36 :workflows,
37 :custom_fields,
37 :custom_fields,
38 :custom_values,
38 :custom_values,
39 :custom_fields_projects,
39 :custom_fields_projects,
40 :custom_fields_trackers,
40 :custom_fields_trackers,
41 :time_entries,
41 :time_entries,
42 :journals,
42 :journals,
43 :journal_details,
43 :journal_details,
44 :queries,
44 :queries,
45 :repositories,
45 :repositories,
46 :changesets
46 :changesets
47
47
48 include Redmine::I18n
48 include Redmine::I18n
49
49
50 def setup
50 def setup
51 User.current = nil
51 User.current = nil
52 end
52 end
53
53
54 def test_index
54 def test_index
55 with_settings :default_language => "en" do
55 with_settings :default_language => "en" do
56 get :index
56 get :index
57 assert_response :success
57 assert_response :success
58
58
59 # links to visible issues
59 # links to visible issues
60 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
60 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
61 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
61 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
62 # private projects hidden
62 # private projects hidden
63 assert_select 'a[href="/issues/6"]', 0
63 assert_select 'a[href="/issues/6"]', 0
64 assert_select 'a[href="/issues/4"]', 0
64 assert_select 'a[href="/issues/4"]', 0
65 # project column
65 # project column
66 assert_select 'th', :text => /Project/
66 assert_select 'th', :text => /Project/
67 end
67 end
68 end
68 end
69
69
70 def test_index_should_not_list_issues_when_module_disabled
70 def test_index_should_not_list_issues_when_module_disabled
71 EnabledModule.where("name = 'issue_tracking' AND project_id = 1").delete_all
71 EnabledModule.where("name = 'issue_tracking' AND project_id = 1").delete_all
72 get :index
72 get :index
73 assert_response :success
73 assert_response :success
74
74
75 assert_select 'a[href="/issues/1"]', 0
75 assert_select 'a[href="/issues/1"]', 0
76 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
76 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
77 end
77 end
78
78
79 def test_index_should_list_visible_issues_only
79 def test_index_should_list_visible_issues_only
80 get :index, :per_page => 100
80 get :index, :per_page => 100
81 assert_response :success
81 assert_response :success
82
82
83 Issue.open.each do |issue|
83 Issue.open.each do |issue|
84 assert_select "tr#issue-#{issue.id}", issue.visible? ? 1 : 0
84 assert_select "tr#issue-#{issue.id}", issue.visible? ? 1 : 0
85 end
85 end
86 end
86 end
87
87
88 def test_index_with_project
88 def test_index_with_project
89 Setting.display_subprojects_issues = 0
89 Setting.display_subprojects_issues = 0
90 get :index, :project_id => 1
90 get :index, :project_id => 1
91 assert_response :success
91 assert_response :success
92
92
93 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
93 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
94 assert_select 'a[href="/issues/5"]', 0
94 assert_select 'a[href="/issues/5"]', 0
95 end
95 end
96
96
97 def test_index_with_project_and_subprojects
97 def test_index_with_project_and_subprojects
98 Setting.display_subprojects_issues = 1
98 Setting.display_subprojects_issues = 1
99 get :index, :project_id => 1
99 get :index, :project_id => 1
100 assert_response :success
100 assert_response :success
101
101
102 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
102 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
103 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
103 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
104 assert_select 'a[href="/issues/6"]', 0
104 assert_select 'a[href="/issues/6"]', 0
105 end
105 end
106
106
107 def test_index_with_project_and_subprojects_should_show_private_subprojects_with_permission
107 def test_index_with_project_and_subprojects_should_show_private_subprojects_with_permission
108 @request.session[:user_id] = 2
108 @request.session[:user_id] = 2
109 Setting.display_subprojects_issues = 1
109 Setting.display_subprojects_issues = 1
110 get :index, :project_id => 1
110 get :index, :project_id => 1
111 assert_response :success
111 assert_response :success
112
112
113 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
113 assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
114 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
114 assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
115 assert_select 'a[href="/issues/6"]', :text => /Issue of a private subproject/
115 assert_select 'a[href="/issues/6"]', :text => /Issue of a private subproject/
116 end
116 end
117
117
118 def test_index_with_project_and_default_filter
118 def test_index_with_project_and_default_filter
119 get :index, :project_id => 1, :set_filter => 1
119 get :index, :project_id => 1, :set_filter => 1
120 assert_response :success
120 assert_response :success
121
121
122 # default filter
122 # default filter
123 assert_query_filters [['status_id', 'o', '']]
123 assert_query_filters [['status_id', 'o', '']]
124 end
124 end
125
125
126 def test_index_with_project_and_filter
126 def test_index_with_project_and_filter
127 get :index, :project_id => 1, :set_filter => 1,
127 get :index, :project_id => 1, :set_filter => 1,
128 :f => ['tracker_id'],
128 :f => ['tracker_id'],
129 :op => {'tracker_id' => '='},
129 :op => {'tracker_id' => '='},
130 :v => {'tracker_id' => ['1']}
130 :v => {'tracker_id' => ['1']}
131 assert_response :success
131 assert_response :success
132
132
133 assert_query_filters [['tracker_id', '=', '1']]
133 assert_query_filters [['tracker_id', '=', '1']]
134 end
134 end
135
135
136 def test_index_with_short_filters
136 def test_index_with_short_filters
137 to_test = {
137 to_test = {
138 'status_id' => {
138 'status_id' => {
139 'o' => { :op => 'o', :values => [''] },
139 'o' => { :op => 'o', :values => [''] },
140 'c' => { :op => 'c', :values => [''] },
140 'c' => { :op => 'c', :values => [''] },
141 '7' => { :op => '=', :values => ['7'] },
141 '7' => { :op => '=', :values => ['7'] },
142 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
142 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
143 '=7' => { :op => '=', :values => ['7'] },
143 '=7' => { :op => '=', :values => ['7'] },
144 '!3' => { :op => '!', :values => ['3'] },
144 '!3' => { :op => '!', :values => ['3'] },
145 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
145 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
146 'subject' => {
146 'subject' => {
147 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
147 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
148 'o' => { :op => '=', :values => ['o'] },
148 'o' => { :op => '=', :values => ['o'] },
149 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
149 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
150 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
150 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
151 'tracker_id' => {
151 'tracker_id' => {
152 '3' => { :op => '=', :values => ['3'] },
152 '3' => { :op => '=', :values => ['3'] },
153 '=3' => { :op => '=', :values => ['3'] }},
153 '=3' => { :op => '=', :values => ['3'] }},
154 'start_date' => {
154 'start_date' => {
155 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
155 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
156 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
156 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
157 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
157 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
158 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
158 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
159 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
159 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
160 '<t+2' => { :op => '<t+', :values => ['2'] },
160 '<t+2' => { :op => '<t+', :values => ['2'] },
161 '>t+2' => { :op => '>t+', :values => ['2'] },
161 '>t+2' => { :op => '>t+', :values => ['2'] },
162 't+2' => { :op => 't+', :values => ['2'] },
162 't+2' => { :op => 't+', :values => ['2'] },
163 't' => { :op => 't', :values => [''] },
163 't' => { :op => 't', :values => [''] },
164 'w' => { :op => 'w', :values => [''] },
164 'w' => { :op => 'w', :values => [''] },
165 '>t-2' => { :op => '>t-', :values => ['2'] },
165 '>t-2' => { :op => '>t-', :values => ['2'] },
166 '<t-2' => { :op => '<t-', :values => ['2'] },
166 '<t-2' => { :op => '<t-', :values => ['2'] },
167 't-2' => { :op => 't-', :values => ['2'] }},
167 't-2' => { :op => 't-', :values => ['2'] }},
168 'created_on' => {
168 'created_on' => {
169 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
169 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
170 '<t-2' => { :op => '<t-', :values => ['2'] },
170 '<t-2' => { :op => '<t-', :values => ['2'] },
171 '>t-2' => { :op => '>t-', :values => ['2'] },
171 '>t-2' => { :op => '>t-', :values => ['2'] },
172 't-2' => { :op => 't-', :values => ['2'] }},
172 't-2' => { :op => 't-', :values => ['2'] }},
173 'cf_1' => {
173 'cf_1' => {
174 'c' => { :op => '=', :values => ['c'] },
174 'c' => { :op => '=', :values => ['c'] },
175 '!c' => { :op => '!', :values => ['c'] },
175 '!c' => { :op => '!', :values => ['c'] },
176 '!*' => { :op => '!*', :values => [''] },
176 '!*' => { :op => '!*', :values => [''] },
177 '*' => { :op => '*', :values => [''] }},
177 '*' => { :op => '*', :values => [''] }},
178 'estimated_hours' => {
178 'estimated_hours' => {
179 '=13.4' => { :op => '=', :values => ['13.4'] },
179 '=13.4' => { :op => '=', :values => ['13.4'] },
180 '>=45' => { :op => '>=', :values => ['45'] },
180 '>=45' => { :op => '>=', :values => ['45'] },
181 '<=125' => { :op => '<=', :values => ['125'] },
181 '<=125' => { :op => '<=', :values => ['125'] },
182 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
182 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
183 '!*' => { :op => '!*', :values => [''] },
183 '!*' => { :op => '!*', :values => [''] },
184 '*' => { :op => '*', :values => [''] }}
184 '*' => { :op => '*', :values => [''] }}
185 }
185 }
186
186
187 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
187 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
188
188
189 to_test.each do |field, expression_and_expected|
189 to_test.each do |field, expression_and_expected|
190 expression_and_expected.each do |filter_expression, expected|
190 expression_and_expected.each do |filter_expression, expected|
191
191
192 get :index, :set_filter => 1, field => filter_expression
192 get :index, :set_filter => 1, field => filter_expression
193 assert_response :success
193 assert_response :success
194
194
195 expected_with_default = default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}})
195 expected_with_default = default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}})
196 assert_query_filters expected_with_default.map {|f, v| [f, v[:operator], v[:values]]}
196 assert_query_filters expected_with_default.map {|f, v| [f, v[:operator], v[:values]]}
197 end
197 end
198 end
198 end
199 end
199 end
200
200
201 def test_index_with_project_and_empty_filters
201 def test_index_with_project_and_empty_filters
202 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
202 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
203 assert_response :success
203 assert_response :success
204
204
205 # no filter
205 # no filter
206 assert_query_filters []
206 assert_query_filters []
207 end
207 end
208
208
209 def test_index_with_project_custom_field_filter
209 def test_index_with_project_custom_field_filter
210 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
210 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
211 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
211 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
212 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
212 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
213 filter_name = "project.cf_#{field.id}"
213 filter_name = "project.cf_#{field.id}"
214 @request.session[:user_id] = 1
214 @request.session[:user_id] = 1
215
215
216 get :index, :set_filter => 1,
216 get :index, :set_filter => 1,
217 :f => [filter_name],
217 :f => [filter_name],
218 :op => {filter_name => '='},
218 :op => {filter_name => '='},
219 :v => {filter_name => ['Foo']},
219 :v => {filter_name => ['Foo']},
220 :c => ['project']
220 :c => ['project']
221 assert_response :success
221 assert_response :success
222
222
223 assert_equal [3, 5], issues_in_list.map(&:project_id).uniq.sort
223 assert_equal [3, 5], issues_in_list.map(&:project_id).uniq.sort
224 end
224 end
225
225
226 def test_index_with_query
226 def test_index_with_query
227 get :index, :project_id => 1, :query_id => 5
227 get :index, :project_id => 1, :query_id => 5
228 assert_response :success
228 assert_response :success
229 end
229 end
230
230
231 def test_index_with_query_grouped_by_tracker
231 def test_index_with_query_grouped_by_tracker
232 get :index, :project_id => 1, :query_id => 6
232 get :index, :project_id => 1, :query_id => 6
233 assert_response :success
233 assert_response :success
234 assert_select 'tr.group span.count'
234 assert_select 'tr.group span.count'
235 end
235 end
236
236
237 def test_index_with_query_grouped_and_sorted_by_category
237 def test_index_with_query_grouped_and_sorted_by_category
238 get :index, :project_id => 1, :set_filter => 1, :group_by => "category", :sort => "category"
238 get :index, :project_id => 1, :set_filter => 1, :group_by => "category", :sort => "category"
239 assert_response :success
239 assert_response :success
240 assert_select 'tr.group span.count'
240 assert_select 'tr.group span.count'
241 end
241 end
242
242
243 def test_index_with_query_grouped_and_sorted_by_fixed_version
243 def test_index_with_query_grouped_and_sorted_by_fixed_version
244 get :index, :project_id => 1, :set_filter => 1, :group_by => "fixed_version", :sort => "fixed_version"
244 get :index, :project_id => 1, :set_filter => 1, :group_by => "fixed_version", :sort => "fixed_version"
245 assert_response :success
245 assert_response :success
246 assert_select 'tr.group span.count'
246 assert_select 'tr.group span.count'
247 end
247 end
248
248
249 def test_index_with_query_grouped_and_sorted_by_fixed_version_in_reverse_order
249 def test_index_with_query_grouped_and_sorted_by_fixed_version_in_reverse_order
250 get :index, :project_id => 1, :set_filter => 1, :group_by => "fixed_version", :sort => "fixed_version:desc"
250 get :index, :project_id => 1, :set_filter => 1, :group_by => "fixed_version", :sort => "fixed_version:desc"
251 assert_response :success
251 assert_response :success
252 assert_select 'tr.group span.count'
252 assert_select 'tr.group span.count'
253 end
253 end
254
254
255 def test_index_with_query_grouped_by_list_custom_field
255 def test_index_with_query_grouped_by_list_custom_field
256 get :index, :project_id => 1, :query_id => 9
256 get :index, :project_id => 1, :query_id => 9
257 assert_response :success
257 assert_response :success
258 assert_select 'tr.group span.count'
258 assert_select 'tr.group span.count'
259 end
259 end
260
260
261 def test_index_with_query_grouped_by_key_value_custom_field
261 def test_index_with_query_grouped_by_key_value_custom_field
262 cf = IssueCustomField.create!(:name => 'Key', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'enumeration')
262 cf = IssueCustomField.create!(:name => 'Key', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'enumeration')
263 cf.enumerations << valueb = CustomFieldEnumeration.new(:name => 'Value B', :position => 1)
263 cf.enumerations << valueb = CustomFieldEnumeration.new(:name => 'Value B', :position => 1)
264 cf.enumerations << valuea = CustomFieldEnumeration.new(:name => 'Value A', :position => 2)
264 cf.enumerations << valuea = CustomFieldEnumeration.new(:name => 'Value A', :position => 2)
265 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => valueb.id)
265 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => valueb.id)
266 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => valueb.id)
266 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => valueb.id)
267 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => valuea.id)
267 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => valuea.id)
268 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
268 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
269
269
270 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
270 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
271 assert_response :success
271 assert_response :success
272
272
273 assert_select 'tr.group', 3
273 assert_select 'tr.group', 3
274 assert_select 'tr.group' do
274 assert_select 'tr.group' do
275 assert_select 'span.name', :text => 'Value B'
275 assert_select 'span.name', :text => 'Value B'
276 assert_select 'span.count', :text => '2'
276 assert_select 'span.count', :text => '2'
277 end
277 end
278 assert_select 'tr.group' do
278 assert_select 'tr.group' do
279 assert_select 'span.name', :text => 'Value A'
279 assert_select 'span.name', :text => 'Value A'
280 assert_select 'span.count', :text => '1'
280 assert_select 'span.count', :text => '1'
281 end
281 end
282 end
282 end
283
283
284 def test_index_with_query_grouped_by_user_custom_field
284 def test_index_with_query_grouped_by_user_custom_field
285 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
285 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
286 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
286 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
287 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
287 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
288 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
288 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
289 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
289 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
290
290
291 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
291 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
292 assert_response :success
292 assert_response :success
293
293
294 assert_select 'tr.group', 3
294 assert_select 'tr.group', 3
295 assert_select 'tr.group' do
295 assert_select 'tr.group' do
296 assert_select 'a', :text => 'John Smith'
296 assert_select 'a', :text => 'John Smith'
297 assert_select 'span.count', :text => '1'
297 assert_select 'span.count', :text => '1'
298 end
298 end
299 assert_select 'tr.group' do
299 assert_select 'tr.group' do
300 assert_select 'a', :text => 'Dave Lopper'
300 assert_select 'a', :text => 'Dave Lopper'
301 assert_select 'span.count', :text => '2'
301 assert_select 'span.count', :text => '2'
302 end
302 end
303 end
303 end
304
304
305 def test_index_grouped_by_boolean_custom_field_should_distinguish_blank_and_false_values
305 def test_index_grouped_by_boolean_custom_field_should_distinguish_blank_and_false_values
306 cf = IssueCustomField.create!(:name => 'Bool', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'bool')
306 cf = IssueCustomField.create!(:name => 'Bool', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'bool')
307 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '1')
307 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '1')
308 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
308 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
309 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '')
309 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '')
310
310
311 with_settings :default_language => 'en' do
311 with_settings :default_language => 'en' do
312 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
312 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
313 assert_response :success
313 assert_response :success
314 end
314 end
315
315
316 assert_select 'tr.group', 3
316 assert_select 'tr.group', 3
317 assert_select 'tr.group', :text => /Yes/
317 assert_select 'tr.group', :text => /Yes/
318 assert_select 'tr.group', :text => /No/
318 assert_select 'tr.group', :text => /No/
319 assert_select 'tr.group', :text => /blank/
319 assert_select 'tr.group', :text => /blank/
320 end
320 end
321
321
322 def test_index_grouped_by_boolean_custom_field_with_false_group_in_first_position_should_show_the_group
322 def test_index_grouped_by_boolean_custom_field_with_false_group_in_first_position_should_show_the_group
323 cf = IssueCustomField.create!(:name => 'Bool', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'bool', :is_filter => true)
323 cf = IssueCustomField.create!(:name => 'Bool', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'bool', :is_filter => true)
324 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '0')
324 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '0')
325 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
325 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
326
326
327 with_settings :default_language => 'en' do
327 with_settings :default_language => 'en' do
328 get :index, :project_id => 1, :set_filter => 1, "cf_#{cf.id}" => "*", :group_by => "cf_#{cf.id}"
328 get :index, :project_id => 1, :set_filter => 1, "cf_#{cf.id}" => "*", :group_by => "cf_#{cf.id}"
329 assert_response :success
329 assert_response :success
330 end
330 end
331
331
332 assert_equal [1, 2], issues_in_list.map(&:id).sort
332 assert_equal [1, 2], issues_in_list.map(&:id).sort
333 assert_select 'tr.group', 1
333 assert_select 'tr.group', 1
334 assert_select 'tr.group', :text => /No/
334 assert_select 'tr.group', :text => /No/
335 end
335 end
336
336
337 def test_index_with_query_grouped_by_tracker_in_normal_order
337 def test_index_with_query_grouped_by_tracker_in_normal_order
338 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
338 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
339
339
340 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc'
340 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc'
341 assert_response :success
341 assert_response :success
342
342
343 assert_equal ["Bug", "Feature request", "Support request"],
343 assert_equal ["Bug", "Feature request", "Support request"],
344 css_select("tr.issue td.tracker").map(&:text).uniq
344 css_select("tr.issue td.tracker").map(&:text).uniq
345 end
345 end
346
346
347 def test_index_with_query_grouped_by_tracker_in_reverse_order
347 def test_index_with_query_grouped_by_tracker_in_reverse_order
348 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
348 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
349
349
350 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc,tracker:desc'
350 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc,tracker:desc'
351 assert_response :success
351 assert_response :success
352
352
353 assert_equal ["Bug", "Feature request", "Support request"].reverse,
353 assert_equal ["Bug", "Feature request", "Support request"].reverse,
354 css_select("tr.issue td.tracker").map(&:text).uniq
354 css_select("tr.issue td.tracker").map(&:text).uniq
355 end
355 end
356
356
357 def test_index_with_query_id_and_project_id_should_set_session_query
357 def test_index_with_query_id_and_project_id_should_set_session_query
358 get :index, :project_id => 1, :query_id => 4
358 get :index, :project_id => 1, :query_id => 4
359 assert_response :success
359 assert_response :success
360 assert_kind_of Hash, session[:issue_query]
360 assert_kind_of Hash, session[:issue_query]
361 assert_equal 4, session[:issue_query][:id]
361 assert_equal 4, session[:issue_query][:id]
362 assert_equal 1, session[:issue_query][:project_id]
362 assert_equal 1, session[:issue_query][:project_id]
363 end
363 end
364
364
365 def test_index_with_invalid_query_id_should_respond_404
365 def test_index_with_invalid_query_id_should_respond_404
366 get :index, :project_id => 1, :query_id => 999
366 get :index, :project_id => 1, :query_id => 999
367 assert_response 404
367 assert_response 404
368 end
368 end
369
369
370 def test_index_with_cross_project_query_in_session_should_show_project_issues
370 def test_index_with_cross_project_query_in_session_should_show_project_issues
371 q = IssueQuery.create!(:name => "cross_project_query", :user_id => 2, :project => nil, :column_names => ['project'])
371 q = IssueQuery.create!(:name => "cross_project_query", :user_id => 2, :project => nil, :column_names => ['project'])
372 @request.session[:issue_query] = {:id => q.id, :project_id => 1}
372 @request.session[:issue_query] = {:id => q.id, :project_id => 1}
373
373
374 with_settings :display_subprojects_issues => '0' do
374 with_settings :display_subprojects_issues => '0' do
375 get :index, :project_id => 1
375 get :index, :project_id => 1
376 end
376 end
377 assert_response :success
377 assert_response :success
378
378
379 assert_select 'h2', :text => q.name
379 assert_select 'h2', :text => q.name
380 assert_equal ["eCookbook"], css_select("tr.issue td.project").map(&:text).uniq
380 assert_equal ["eCookbook"], css_select("tr.issue td.project").map(&:text).uniq
381 end
381 end
382
382
383 def test_private_query_should_not_be_available_to_other_users
383 def test_private_query_should_not_be_available_to_other_users
384 q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
384 q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
385 @request.session[:user_id] = 3
385 @request.session[:user_id] = 3
386
386
387 get :index, :query_id => q.id
387 get :index, :query_id => q.id
388 assert_response 403
388 assert_response 403
389 end
389 end
390
390
391 def test_private_query_should_be_available_to_its_user
391 def test_private_query_should_be_available_to_its_user
392 q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
392 q = IssueQuery.create!(:name => "private", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PRIVATE, :project => nil)
393 @request.session[:user_id] = 2
393 @request.session[:user_id] = 2
394
394
395 get :index, :query_id => q.id
395 get :index, :query_id => q.id
396 assert_response :success
396 assert_response :success
397 end
397 end
398
398
399 def test_public_query_should_be_available_to_other_users
399 def test_public_query_should_be_available_to_other_users
400 q = IssueQuery.create!(:name => "public", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PUBLIC, :project => nil)
400 q = IssueQuery.create!(:name => "public", :user => User.find(2), :visibility => IssueQuery::VISIBILITY_PUBLIC, :project => nil)
401 @request.session[:user_id] = 3
401 @request.session[:user_id] = 3
402
402
403 get :index, :query_id => q.id
403 get :index, :query_id => q.id
404 assert_response :success
404 assert_response :success
405 end
405 end
406
406
407 def test_index_should_omit_page_param_in_export_links
407 def test_index_should_omit_page_param_in_export_links
408 get :index, :page => 2
408 get :index, :page => 2
409 assert_response :success
409 assert_response :success
410 assert_select 'a.atom[href="/issues.atom"]'
410 assert_select 'a.atom[href="/issues.atom"]'
411 assert_select 'a.csv[href="/issues.csv"]'
411 assert_select 'a.csv[href="/issues.csv"]'
412 assert_select 'a.pdf[href="/issues.pdf"]'
412 assert_select 'a.pdf[href="/issues.pdf"]'
413 assert_select 'form#csv-export-form[action="/issues.csv"]'
413 assert_select 'form#csv-export-form[action="/issues.csv"]'
414 end
414 end
415
415
416 def test_index_should_not_warn_when_not_exceeding_export_limit
416 def test_index_should_not_warn_when_not_exceeding_export_limit
417 with_settings :issues_export_limit => 200 do
417 with_settings :issues_export_limit => 200 do
418 get :index
418 get :index
419 assert_select '#csv-export-options p.icon-warning', 0
419 assert_select '#csv-export-options p.icon-warning', 0
420 end
420 end
421 end
421 end
422
422
423 def test_index_should_warn_when_exceeding_export_limit
423 def test_index_should_warn_when_exceeding_export_limit
424 with_settings :issues_export_limit => 2 do
424 with_settings :issues_export_limit => 2 do
425 get :index
425 get :index
426 assert_select '#csv-export-options p.icon-warning', :text => %r{limit: 2}
426 assert_select '#csv-export-options p.icon-warning', :text => %r{limit: 2}
427 end
427 end
428 end
428 end
429
429
430 def test_index_should_include_query_params_as_hidden_fields_in_csv_export_form
430 def test_index_should_include_query_params_as_hidden_fields_in_csv_export_form
431 get :index, :project_id => 1, :set_filter => "1", :tracker_id => "2", :sort => 'status', :c => ["status", "priority"]
431 get :index, :project_id => 1, :set_filter => "1", :tracker_id => "2", :sort => 'status', :c => ["status", "priority"]
432
432
433 assert_select '#csv-export-form[action=?]', '/projects/ecookbook/issues.csv'
433 assert_select '#csv-export-form[action=?]', '/projects/ecookbook/issues.csv'
434 assert_select '#csv-export-form[method=?]', 'get'
434 assert_select '#csv-export-form[method=?]', 'get'
435
435
436 assert_select '#csv-export-form' do
436 assert_select '#csv-export-form' do
437 assert_select 'input[name=?][value=?]', 'set_filter', '1'
437 assert_select 'input[name=?][value=?]', 'set_filter', '1'
438
438
439 assert_select 'input[name=?][value=?]', 'f[]', 'tracker_id'
439 assert_select 'input[name=?][value=?]', 'f[]', 'tracker_id'
440 assert_select 'input[name=?][value=?]', 'op[tracker_id]', '='
440 assert_select 'input[name=?][value=?]', 'op[tracker_id]', '='
441 assert_select 'input[name=?][value=?]', 'v[tracker_id][]', '2'
441 assert_select 'input[name=?][value=?]', 'v[tracker_id][]', '2'
442
442
443 assert_select 'input[name=?][value=?]', 'c[]', 'status'
443 assert_select 'input[name=?][value=?]', 'c[]', 'status'
444 assert_select 'input[name=?][value=?]', 'c[]', 'priority'
444 assert_select 'input[name=?][value=?]', 'c[]', 'priority'
445
445
446 assert_select 'input[name=?][value=?]', 'sort', 'status'
446 assert_select 'input[name=?][value=?]', 'sort', 'status'
447 end
447 end
448
448
449 get :index, :project_id => 1, :set_filter => "1", :f => []
449 get :index, :project_id => 1, :set_filter => "1", :f => []
450 assert_select '#csv-export-form input[name=?][value=?]', 'f[]', ''
450 assert_select '#csv-export-form input[name=?][value=?]', 'f[]', ''
451 end
451 end
452
452
453 def test_index_csv
453 def test_index_csv
454 get :index, :format => 'csv'
454 get :index, :format => 'csv'
455 assert_response :success
455 assert_response :success
456
456
457 assert_equal 'text/csv; header=present', @response.content_type
457 assert_equal 'text/csv; header=present', @response.content_type
458 assert response.body.starts_with?("#,")
458 assert response.body.starts_with?("#,")
459 lines = response.body.chomp.split("\n")
459 lines = response.body.chomp.split("\n")
460 # default columns + id and project
460 # default columns + id and project
461 assert_equal Setting.issue_list_default_columns.size + 2, lines[0].split(',').size
461 assert_equal Setting.issue_list_default_columns.size + 2, lines[0].split(',').size
462 end
462 end
463
463
464 def test_index_csv_with_project
464 def test_index_csv_with_project
465 get :index, :project_id => 1, :format => 'csv'
465 get :index, :project_id => 1, :format => 'csv'
466 assert_response :success
466 assert_response :success
467
467
468 assert_equal 'text/csv; header=present', @response.content_type
468 assert_equal 'text/csv; header=present', @response.content_type
469 end
469 end
470
470
471 def test_index_csv_without_any_filters
471 def test_index_csv_without_any_filters
472 @request.session[:user_id] = 1
472 @request.session[:user_id] = 1
473 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5, :subject => 'Closed issue', :author_id => 1)
473 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5, :subject => 'Closed issue', :author_id => 1)
474 get :index, :set_filter => 1, :f => [], :format => 'csv'
474 get :index, :set_filter => 1, :f => [], :format => 'csv'
475 assert_response :success
475 assert_response :success
476 # -1 for headers
476 # -1 for headers
477 assert_equal Issue.count, response.body.chomp.split("\n").size - 1
477 assert_equal Issue.count, response.body.chomp.split("\n").size - 1
478 end
478 end
479
479
480 def test_index_csv_with_description
480 def test_index_csv_with_description
481 Issue.generate!(:description => 'test_index_csv_with_description')
481 Issue.generate!(:description => 'test_index_csv_with_description')
482
482
483 with_settings :default_language => 'en' do
483 with_settings :default_language => 'en' do
484 get :index, :format => 'csv', :csv => {:description => '1'}
484 get :index, :format => 'csv', :csv => {:description => '1'}
485 assert_response :success
485 assert_response :success
486 end
486 end
487
487
488 assert_equal 'text/csv; header=present', response.content_type
488 assert_equal 'text/csv; header=present', response.content_type
489 headers = response.body.chomp.split("\n").first.split(',')
489 headers = response.body.chomp.split("\n").first.split(',')
490 assert_include 'Description', headers
490 assert_include 'Description', headers
491 assert_include 'test_index_csv_with_description', response.body
491 assert_include 'test_index_csv_with_description', response.body
492 end
492 end
493
493
494 def test_index_csv_with_spent_time_column
494 def test_index_csv_with_spent_time_column
495 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :subject => 'test_index_csv_with_spent_time_column', :author_id => 2)
495 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :subject => 'test_index_csv_with_spent_time_column', :author_id => 2)
496 TimeEntry.create!(:project => issue.project, :issue => issue, :hours => 7.33, :user => User.find(2), :spent_on => Date.today)
496 TimeEntry.create!(:project => issue.project, :issue => issue, :hours => 7.33, :user => User.find(2), :spent_on => Date.today)
497
497
498 get :index, :format => 'csv', :set_filter => '1', :c => %w(subject spent_hours)
498 get :index, :format => 'csv', :set_filter => '1', :c => %w(subject spent_hours)
499 assert_response :success
499 assert_response :success
500 assert_equal 'text/csv; header=present', @response.content_type
500 assert_equal 'text/csv; header=present', @response.content_type
501 lines = @response.body.chomp.split("\n")
501 lines = @response.body.chomp.split("\n")
502 assert_include "#{issue.id},#{issue.subject},7.33", lines
502 assert_include "#{issue.id},#{issue.subject},7.33", lines
503 end
503 end
504
504
505 def test_index_csv_with_all_columns
505 def test_index_csv_with_all_columns
506 get :index, :format => 'csv', :csv => {:columns => 'all'}
506 get :index, :format => 'csv', :csv => {:columns => 'all'}
507 assert_response :success
507 assert_response :success
508
508
509 assert_equal 'text/csv; header=present', @response.content_type
509 assert_equal 'text/csv; header=present', @response.content_type
510 assert_match /\A#,/, response.body
510 assert_match /\A#,/, response.body
511 lines = response.body.chomp.split("\n")
511 lines = response.body.chomp.split("\n")
512 assert_equal IssueQuery.new.available_inline_columns.size, lines[0].split(',').size
512 assert_equal IssueQuery.new.available_inline_columns.size, lines[0].split(',').size
513 end
513 end
514
514
515 def test_index_csv_with_multi_column_field
515 def test_index_csv_with_multi_column_field
516 CustomField.find(1).update_attribute :multiple, true
516 CustomField.find(1).update_attribute :multiple, true
517 issue = Issue.find(1)
517 issue = Issue.find(1)
518 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
518 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
519 issue.save!
519 issue.save!
520
520
521 get :index, :format => 'csv', :csv => {:columns => 'all'}
521 get :index, :format => 'csv', :csv => {:columns => 'all'}
522 assert_response :success
522 assert_response :success
523 lines = @response.body.chomp.split("\n")
523 lines = @response.body.chomp.split("\n")
524 assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
524 assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
525 end
525 end
526
526
527 def test_index_csv_should_format_float_custom_fields_with_csv_decimal_separator
527 def test_index_csv_should_format_float_custom_fields_with_csv_decimal_separator
528 field = IssueCustomField.create!(:name => 'Float', :is_for_all => true, :tracker_ids => [1], :field_format => 'float')
528 field = IssueCustomField.create!(:name => 'Float', :is_for_all => true, :tracker_ids => [1], :field_format => 'float')
529 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id => '185.6'})
529 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id => '185.6'})
530
530
531 with_settings :default_language => 'fr' do
531 with_settings :default_language => 'fr' do
532 get :index, :format => 'csv', :csv => {:columns => 'all'}
532 get :index, :format => 'csv', :csv => {:columns => 'all'}
533 assert_response :success
533 assert_response :success
534 issue_line = response.body.chomp.split("\n").map {|line| line.split(';')}.detect {|line| line[0]==issue.id.to_s}
534 issue_line = response.body.chomp.split("\n").map {|line| line.split(';')}.detect {|line| line[0]==issue.id.to_s}
535 assert_include '185,60', issue_line
535 assert_include '185,60', issue_line
536 end
536 end
537
537
538 with_settings :default_language => 'en' do
538 with_settings :default_language => 'en' do
539 get :index, :format => 'csv', :csv => {:columns => 'all'}
539 get :index, :format => 'csv', :csv => {:columns => 'all'}
540 assert_response :success
540 assert_response :success
541 issue_line = response.body.chomp.split("\n").map {|line| line.split(',')}.detect {|line| line[0]==issue.id.to_s}
541 issue_line = response.body.chomp.split("\n").map {|line| line.split(',')}.detect {|line| line[0]==issue.id.to_s}
542 assert_include '185.60', issue_line
542 assert_include '185.60', issue_line
543 end
543 end
544 end
544 end
545
545
546 def test_index_csv_should_fill_parent_column_with_parent_id
546 def test_index_csv_should_fill_parent_column_with_parent_id
547 Issue.delete_all
547 Issue.delete_all
548 parent = Issue.generate!
548 parent = Issue.generate!
549 child = Issue.generate!(:parent_issue_id => parent.id)
549 child = Issue.generate!(:parent_issue_id => parent.id)
550
550
551 with_settings :default_language => 'en' do
551 with_settings :default_language => 'en' do
552 get :index, :format => 'csv', :c => %w(parent)
552 get :index, :format => 'csv', :c => %w(parent)
553 end
553 end
554 lines = response.body.split("\n")
554 lines = response.body.split("\n")
555 assert_include "#{child.id},#{parent.id}", lines
555 assert_include "#{child.id},#{parent.id}", lines
556 end
556 end
557
557
558 def test_index_csv_big_5
558 def test_index_csv_big_5
559 with_settings :default_language => "zh-TW" do
559 with_settings :default_language => "zh-TW" do
560 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88".force_encoding('UTF-8')
560 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88".force_encoding('UTF-8')
561 str_big5 = "\xa4@\xa4\xeb".force_encoding('Big5')
561 str_big5 = "\xa4@\xa4\xeb".force_encoding('Big5')
562 issue = Issue.generate!(:subject => str_utf8)
562 issue = Issue.generate!(:subject => str_utf8)
563
563
564 get :index, :project_id => 1,
564 get :index, :project_id => 1,
565 :f => ['subject'],
565 :f => ['subject'],
566 :op => '=', :values => [str_utf8],
566 :op => '=', :values => [str_utf8],
567 :format => 'csv'
567 :format => 'csv'
568 assert_equal 'text/csv; header=present', @response.content_type
568 assert_equal 'text/csv; header=present', @response.content_type
569 lines = @response.body.chomp.split("\n")
569 lines = @response.body.chomp.split("\n")
570 header = lines[0]
570 header = lines[0]
571 status = "\xaa\xac\xbaA".force_encoding('Big5')
571 status = "\xaa\xac\xbaA".force_encoding('Big5')
572 assert_include status, header
572 assert_include status, header
573 issue_line = lines.find {|l| l =~ /^#{issue.id},/}
573 issue_line = lines.find {|l| l =~ /^#{issue.id},/}
574 assert_include str_big5, issue_line
574 assert_include str_big5, issue_line
575 end
575 end
576 end
576 end
577
577
578 def test_index_csv_cannot_convert_should_be_replaced_big_5
578 def test_index_csv_cannot_convert_should_be_replaced_big_5
579 with_settings :default_language => "zh-TW" do
579 with_settings :default_language => "zh-TW" do
580 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
580 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85".force_encoding('UTF-8')
581 issue = Issue.generate!(:subject => str_utf8)
581 issue = Issue.generate!(:subject => str_utf8)
582
582
583 get :index, :project_id => 1,
583 get :index, :project_id => 1,
584 :f => ['subject'],
584 :f => ['subject'],
585 :op => '=', :values => [str_utf8],
585 :op => '=', :values => [str_utf8],
586 :c => ['status', 'subject'],
586 :c => ['status', 'subject'],
587 :format => 'csv',
587 :format => 'csv',
588 :set_filter => 1
588 :set_filter => 1
589 assert_equal 'text/csv; header=present', @response.content_type
589 assert_equal 'text/csv; header=present', @response.content_type
590 lines = @response.body.chomp.split("\n")
590 lines = @response.body.chomp.split("\n")
591 header = lines[0]
591 header = lines[0]
592 issue_line = lines.find {|l| l =~ /^#{issue.id},/}
592 issue_line = lines.find {|l| l =~ /^#{issue.id},/}
593 s1 = "\xaa\xac\xbaA".force_encoding('Big5') # status
593 s1 = "\xaa\xac\xbaA".force_encoding('Big5') # status
594 assert header.include?(s1)
594 assert header.include?(s1)
595 s2 = issue_line.split(",")[2]
595 s2 = issue_line.split(",")[2]
596 s3 = "\xa5H?".force_encoding('Big5') # subject
596 s3 = "\xa5H?".force_encoding('Big5') # subject
597 assert_equal s3, s2
597 assert_equal s3, s2
598 end
598 end
599 end
599 end
600
600
601 def test_index_csv_tw
601 def test_index_csv_tw
602 with_settings :default_language => "zh-TW" do
602 with_settings :default_language => "zh-TW" do
603 str1 = "test_index_csv_tw"
603 str1 = "test_index_csv_tw"
604 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
604 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
605
605
606 get :index, :project_id => 1,
606 get :index, :project_id => 1,
607 :f => ['subject'],
607 :f => ['subject'],
608 :op => '=', :values => [str1],
608 :op => '=', :values => [str1],
609 :c => ['estimated_hours', 'subject'],
609 :c => ['estimated_hours', 'subject'],
610 :format => 'csv',
610 :format => 'csv',
611 :set_filter => 1
611 :set_filter => 1
612 assert_equal 'text/csv; header=present', @response.content_type
612 assert_equal 'text/csv; header=present', @response.content_type
613 lines = @response.body.chomp.split("\n")
613 lines = @response.body.chomp.split("\n")
614 assert_include "#{issue.id},1234.50,#{str1}", lines
614 assert_include "#{issue.id},1234.50,#{str1}", lines
615 end
615 end
616 end
616 end
617
617
618 def test_index_csv_fr
618 def test_index_csv_fr
619 with_settings :default_language => "fr" do
619 with_settings :default_language => "fr" do
620 str1 = "test_index_csv_fr"
620 str1 = "test_index_csv_fr"
621 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
621 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
622
622
623 get :index, :project_id => 1,
623 get :index, :project_id => 1,
624 :f => ['subject'],
624 :f => ['subject'],
625 :op => '=', :values => [str1],
625 :op => '=', :values => [str1],
626 :c => ['estimated_hours', 'subject'],
626 :c => ['estimated_hours', 'subject'],
627 :format => 'csv',
627 :format => 'csv',
628 :set_filter => 1
628 :set_filter => 1
629 assert_equal 'text/csv; header=present', @response.content_type
629 assert_equal 'text/csv; header=present', @response.content_type
630 lines = @response.body.chomp.split("\n")
630 lines = @response.body.chomp.split("\n")
631 assert_include "#{issue.id};1234,50;#{str1}", lines
631 assert_include "#{issue.id};1234,50;#{str1}", lines
632 end
632 end
633 end
633 end
634
634
635 def test_index_pdf
635 def test_index_pdf
636 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
636 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
637 with_settings :default_language => lang do
637 with_settings :default_language => lang do
638
638
639 get :index
639 get :index
640 assert_response :success
640 assert_response :success
641
641
642 get :index, :format => 'pdf'
642 get :index, :format => 'pdf'
643 assert_response :success
643 assert_response :success
644 assert_equal 'application/pdf', @response.content_type
644 assert_equal 'application/pdf', @response.content_type
645
645
646 get :index, :project_id => 1, :format => 'pdf'
646 get :index, :project_id => 1, :format => 'pdf'
647 assert_response :success
647 assert_response :success
648 assert_equal 'application/pdf', @response.content_type
648 assert_equal 'application/pdf', @response.content_type
649
649
650 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
650 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
651 assert_response :success
651 assert_response :success
652 assert_equal 'application/pdf', @response.content_type
652 assert_equal 'application/pdf', @response.content_type
653 end
653 end
654 end
654 end
655 end
655 end
656
656
657 def test_index_pdf_with_query_grouped_by_list_custom_field
657 def test_index_pdf_with_query_grouped_by_list_custom_field
658 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
658 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
659 assert_response :success
659 assert_response :success
660 assert_equal 'application/pdf', @response.content_type
660 assert_equal 'application/pdf', @response.content_type
661 end
661 end
662
662
663 def test_index_atom
663 def test_index_atom
664 get :index, :project_id => 'ecookbook', :format => 'atom'
664 get :index, :project_id => 'ecookbook', :format => 'atom'
665 assert_response :success
665 assert_response :success
666 assert_equal 'application/atom+xml', response.content_type
666 assert_equal 'application/atom+xml', response.content_type
667
667
668 assert_select 'feed' do
668 assert_select 'feed' do
669 assert_select 'link[rel=self][href=?]', 'http://test.host/projects/ecookbook/issues.atom'
669 assert_select 'link[rel=self][href=?]', 'http://test.host/projects/ecookbook/issues.atom'
670 assert_select 'link[rel=alternate][href=?]', 'http://test.host/projects/ecookbook/issues'
670 assert_select 'link[rel=alternate][href=?]', 'http://test.host/projects/ecookbook/issues'
671 assert_select 'entry link[href=?]', 'http://test.host/issues/1'
671 assert_select 'entry link[href=?]', 'http://test.host/issues/1'
672 end
672 end
673 end
673 end
674
674
675 def test_index_should_include_back_url_input
675 def test_index_should_include_back_url_input
676 get :index, :project_id => 'ecookbook', :foo => 'bar'
676 get :index, :project_id => 'ecookbook', :foo => 'bar'
677 assert_response :success
677 assert_response :success
678 assert_select 'input[name=back_url][value=?]', '/projects/ecookbook/issues?foo=bar'
678 assert_select 'input[name=back_url][value=?]', '/projects/ecookbook/issues?foo=bar'
679 end
679 end
680
680
681 def test_index_sort
681 def test_index_sort
682 get :index, :sort => 'tracker,id:desc'
682 get :index, :sort => 'tracker,id:desc'
683 assert_response :success
683 assert_response :success
684
684
685 sort_params = @request.session['issues_index_sort']
685 sort_params = @request.session['issues_index_sort']
686 assert sort_params.is_a?(String)
686 assert sort_params.is_a?(String)
687 assert_equal 'tracker,id:desc', sort_params
687 assert_equal 'tracker,id:desc', sort_params
688
688
689 assert_equal issues_in_list.sort_by {|issue| [issue.tracker.position, -issue.id]}, issues_in_list
689 assert_equal issues_in_list.sort_by {|issue| [issue.tracker.position, -issue.id]}, issues_in_list
690 assert_select 'table.issues.sort-by-tracker.sort-asc'
690 assert_select 'table.issues.sort-by-tracker.sort-asc'
691 end
691 end
692
692
693 def test_index_sort_by_field_not_included_in_columns
693 def test_index_sort_by_field_not_included_in_columns
694 Setting.issue_list_default_columns = %w(subject author)
694 Setting.issue_list_default_columns = %w(subject author)
695 get :index, :sort => 'tracker'
695 get :index, :sort => 'tracker'
696 end
696 end
697
697
698 def test_index_sort_by_assigned_to
698 def test_index_sort_by_assigned_to
699 get :index, :sort => 'assigned_to'
699 get :index, :sort => 'assigned_to'
700 assert_response :success
700 assert_response :success
701
701
702 assignees = issues_in_list.map(&:assigned_to).compact
702 assignees = issues_in_list.map(&:assigned_to).compact
703 assert_equal assignees.sort, assignees
703 assert_equal assignees.sort, assignees
704 assert_select 'table.issues.sort-by-assigned-to.sort-asc'
704 assert_select 'table.issues.sort-by-assigned-to.sort-asc'
705 end
705 end
706
706
707 def test_index_sort_by_assigned_to_desc
707 def test_index_sort_by_assigned_to_desc
708 get :index, :sort => 'assigned_to:desc'
708 get :index, :sort => 'assigned_to:desc'
709 assert_response :success
709 assert_response :success
710
710
711 assignees = issues_in_list.map(&:assigned_to).compact
711 assignees = issues_in_list.map(&:assigned_to).compact
712 assert_equal assignees.sort.reverse, assignees
712 assert_equal assignees.sort.reverse, assignees
713 assert_select 'table.issues.sort-by-assigned-to.sort-desc'
713 assert_select 'table.issues.sort-by-assigned-to.sort-desc'
714 end
714 end
715
715
716 def test_index_group_by_assigned_to
716 def test_index_group_by_assigned_to
717 get :index, :group_by => 'assigned_to', :sort => 'priority'
717 get :index, :group_by => 'assigned_to', :sort => 'priority'
718 assert_response :success
718 assert_response :success
719 end
719 end
720
720
721 def test_index_sort_by_author
721 def test_index_sort_by_author
722 get :index, :sort => 'author', :c => ['author']
722 get :index, :sort => 'author', :c => ['author']
723 assert_response :success
723 assert_response :success
724
724
725 authors = issues_in_list.map(&:author)
725 authors = issues_in_list.map(&:author)
726 assert_equal authors.sort, authors
726 assert_equal authors.sort, authors
727 end
727 end
728
728
729 def test_index_sort_by_author_desc
729 def test_index_sort_by_author_desc
730 get :index, :sort => 'author:desc'
730 get :index, :sort => 'author:desc'
731 assert_response :success
731 assert_response :success
732
732
733 authors = issues_in_list.map(&:author)
733 authors = issues_in_list.map(&:author)
734 assert_equal authors.sort.reverse, authors
734 assert_equal authors.sort.reverse, authors
735 end
735 end
736
736
737 def test_index_group_by_author
737 def test_index_group_by_author
738 get :index, :group_by => 'author', :sort => 'priority'
738 get :index, :group_by => 'author', :sort => 'priority'
739 assert_response :success
739 assert_response :success
740 end
740 end
741
741
742 def test_index_sort_by_spent_hours
742 def test_index_sort_by_spent_hours
743 get :index, :sort => 'spent_hours:desc'
743 get :index, :sort => 'spent_hours:desc'
744 assert_response :success
744 assert_response :success
745 hours = issues_in_list.map(&:spent_hours)
745 hours = issues_in_list.map(&:spent_hours)
746 assert_equal hours.sort.reverse, hours
746 assert_equal hours.sort.reverse, hours
747 end
747 end
748
748
749 def test_index_sort_by_total_spent_hours
749 def test_index_sort_by_total_spent_hours
750 get :index, :sort => 'total_spent_hours:desc'
750 get :index, :sort => 'total_spent_hours:desc'
751 assert_response :success
751 assert_response :success
752 hours = issues_in_list.map(&:total_spent_hours)
752 hours = issues_in_list.map(&:total_spent_hours)
753 assert_equal hours.sort.reverse, hours
753 assert_equal hours.sort.reverse, hours
754 end
754 end
755
755
756 def test_index_sort_by_total_estimated_hours
756 def test_index_sort_by_total_estimated_hours
757 get :index, :sort => 'total_estimated_hours:desc'
757 get :index, :sort => 'total_estimated_hours:desc'
758 assert_response :success
758 assert_response :success
759 hours = issues_in_list.map(&:total_estimated_hours)
759 hours = issues_in_list.map(&:total_estimated_hours)
760 assert_equal hours.sort.reverse, hours
760 assert_equal hours.sort.reverse, hours
761 end
761 end
762
762
763 def test_index_sort_by_user_custom_field
763 def test_index_sort_by_user_custom_field
764 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
764 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
765 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
765 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
766 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
766 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
767 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
767 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
768 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
768 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
769
769
770 get :index, :project_id => 1, :set_filter => 1, :sort => "cf_#{cf.id},id"
770 get :index, :project_id => 1, :set_filter => 1, :sort => "cf_#{cf.id},id"
771 assert_response :success
771 assert_response :success
772
772
773 assert_equal [2, 3, 1], issues_in_list.select {|issue| issue.custom_field_value(cf).present?}.map(&:id)
773 assert_equal [2, 3, 1], issues_in_list.select {|issue| issue.custom_field_value(cf).present?}.map(&:id)
774 end
774 end
775
775
776 def test_index_with_columns
776 def test_index_with_columns
777 columns = ['tracker', 'subject', 'assigned_to']
777 columns = ['tracker', 'subject', 'assigned_to']
778 get :index, :set_filter => 1, :c => columns
778 get :index, :set_filter => 1, :c => columns
779 assert_response :success
779 assert_response :success
780
780
781 # query should use specified columns + id and checkbox
781 # query should use specified columns + id and checkbox
782 assert_select 'table.issues thead th', columns.size + 2
782 assert_select 'table.issues thead th', columns.size + 2
783
783
784 # columns should be stored in session
784 # columns should be stored in session
785 assert_kind_of Hash, session[:issue_query]
785 assert_kind_of Hash, session[:issue_query]
786 assert_kind_of Array, session[:issue_query][:column_names]
786 assert_kind_of Array, session[:issue_query][:column_names]
787 assert_equal columns, session[:issue_query][:column_names].map(&:to_s)
787 assert_equal columns, session[:issue_query][:column_names].map(&:to_s)
788
788
789 # ensure only these columns are kept in the selected columns list
789 # ensure only these columns are kept in the selected columns list
790 assert_select 'select#selected_columns option' do
790 assert_select 'select#selected_columns option' do
791 assert_select 'option', 3
791 assert_select 'option', 3
792 assert_select 'option[value=tracker]'
792 assert_select 'option[value=tracker]'
793 assert_select 'option[value=project]', 0
793 assert_select 'option[value=project]', 0
794 end
794 end
795 end
795 end
796
796
797 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
797 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
798 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
798 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
799 get :index, :set_filter => 1
799 get :index, :set_filter => 1
800
800
801 # query should use specified columns
801 # query should use specified columns
802 assert_equal ["#", "Project", "Tracker", "Subject", "Assignee"], columns_in_issues_list
802 assert_equal ["#", "Project", "Tracker", "Subject", "Assignee"], columns_in_issues_list
803 end
803 end
804
804
805 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
805 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
806 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
806 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
807 columns = ['id', 'tracker', 'subject', 'assigned_to']
807 columns = ['id', 'tracker', 'subject', 'assigned_to']
808 get :index, :set_filter => 1, :c => columns
808 get :index, :set_filter => 1, :c => columns
809
809
810 # query should use specified columns
810 # query should use specified columns
811 assert_equal ["#", "Tracker", "Subject", "Assignee"], columns_in_issues_list
811 assert_equal ["#", "Tracker", "Subject", "Assignee"], columns_in_issues_list
812 end
812 end
813
813
814 def test_index_with_default_columns_should_respect_default_columns_order
814 def test_index_with_default_columns_should_respect_default_columns_order
815 columns = ['assigned_to', 'subject', 'status', 'tracker']
815 columns = ['assigned_to', 'subject', 'status', 'tracker']
816 with_settings :issue_list_default_columns => columns do
816 with_settings :issue_list_default_columns => columns do
817 get :index, :project_id => 1, :set_filter => 1
817 get :index, :project_id => 1, :set_filter => 1
818
818
819 assert_equal ["#", "Assignee", "Subject", "Status", "Tracker"], columns_in_issues_list
819 assert_equal ["#", "Assignee", "Subject", "Status", "Tracker"], columns_in_issues_list
820 end
820 end
821 end
821 end
822
822
823 def test_index_with_custom_field_column
823 def test_index_with_custom_field_column
824 columns = %w(tracker subject cf_2)
824 columns = %w(tracker subject cf_2)
825 get :index, :set_filter => 1, :c => columns
825 get :index, :set_filter => 1, :c => columns
826 assert_response :success
826 assert_response :success
827
827
828 # query should use specified columns
828 # query should use specified columns
829 assert_equal ["#", "Tracker", "Subject", "Searchable field"], columns_in_issues_list
829 assert_equal ["#", "Tracker", "Subject", "Searchable field"], columns_in_issues_list
830 assert_select 'table.issues td.cf_2.string'
830 assert_select 'table.issues td.cf_2.string'
831 end
831 end
832
832
833 def test_index_with_multi_custom_field_column
833 def test_index_with_multi_custom_field_column
834 field = CustomField.find(1)
834 field = CustomField.find(1)
835 field.update_attribute :multiple, true
835 field.update_attribute :multiple, true
836 issue = Issue.find(1)
836 issue = Issue.find(1)
837 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
837 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
838 issue.save!
838 issue.save!
839
839
840 get :index, :set_filter => 1, :c => %w(tracker subject cf_1)
840 get :index, :set_filter => 1, :c => %w(tracker subject cf_1)
841 assert_response :success
841 assert_response :success
842
842
843 assert_select 'table.issues td.cf_1', :text => 'MySQL, Oracle'
843 assert_select 'table.issues td.cf_1', :text => 'MySQL, Oracle'
844 end
844 end
845
845
846 def test_index_with_multi_user_custom_field_column
846 def test_index_with_multi_user_custom_field_column
847 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
847 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
848 :tracker_ids => [1], :is_for_all => true)
848 :tracker_ids => [1], :is_for_all => true)
849 issue = Issue.find(1)
849 issue = Issue.find(1)
850 issue.custom_field_values = {field.id => ['2', '3']}
850 issue.custom_field_values = {field.id => ['2', '3']}
851 issue.save!
851 issue.save!
852
852
853 get :index, :set_filter => 1, :c => ['tracker', 'subject', "cf_#{field.id}"]
853 get :index, :set_filter => 1, :c => ['tracker', 'subject', "cf_#{field.id}"]
854 assert_response :success
854 assert_response :success
855
855
856 assert_select "table.issues td.cf_#{field.id}" do
856 assert_select "table.issues td.cf_#{field.id}" do
857 assert_select 'a', 2
857 assert_select 'a', 2
858 assert_select 'a[href=?]', '/users/2', :text => 'John Smith'
858 assert_select 'a[href=?]', '/users/2', :text => 'John Smith'
859 assert_select 'a[href=?]', '/users/3', :text => 'Dave Lopper'
859 assert_select 'a[href=?]', '/users/3', :text => 'Dave Lopper'
860 end
860 end
861 end
861 end
862
862
863 def test_index_with_date_column
863 def test_index_with_date_column
864 with_settings :date_format => '%d/%m/%Y' do
864 with_settings :date_format => '%d/%m/%Y' do
865 Issue.find(1).update_attribute :start_date, '1987-08-24'
865 Issue.find(1).update_attribute :start_date, '1987-08-24'
866 get :index, :set_filter => 1, :c => %w(start_date)
866 get :index, :set_filter => 1, :c => %w(start_date)
867 assert_select "table.issues td.start_date", :text => '24/08/1987'
867 assert_select "table.issues td.start_date", :text => '24/08/1987'
868 end
868 end
869 end
869 end
870
870
871 def test_index_with_done_ratio_column
871 def test_index_with_done_ratio_column
872 Issue.find(1).update_attribute :done_ratio, 40
872 Issue.find(1).update_attribute :done_ratio, 40
873 get :index, :set_filter => 1, :c => %w(done_ratio)
873 get :index, :set_filter => 1, :c => %w(done_ratio)
874 assert_select 'table.issues td.done_ratio' do
874 assert_select 'table.issues td.done_ratio' do
875 assert_select 'table.progress' do
875 assert_select 'table.progress' do
876 assert_select 'td.closed[style=?]', 'width: 40%;'
876 assert_select 'td.closed[style=?]', 'width: 40%;'
877 end
877 end
878 end
878 end
879 end
879 end
880
880
881 def test_index_with_spent_hours_column
881 def test_index_with_spent_hours_column
882 Issue.expects(:load_visible_spent_hours).once
882 Issue.expects(:load_visible_spent_hours).once
883 get :index, :set_filter => 1, :c => %w(subject spent_hours)
883 get :index, :set_filter => 1, :c => %w(subject spent_hours)
884 assert_select 'table.issues tr#issue-3 td.spent_hours', :text => '1.00'
884 assert_select 'table.issues tr#issue-3 td.spent_hours', :text => '1.00'
885 end
885 end
886
886
887 def test_index_with_total_spent_hours_column
887 def test_index_with_total_spent_hours_column
888 Issue.expects(:load_visible_total_spent_hours).once
888 Issue.expects(:load_visible_total_spent_hours).once
889 get :index, :set_filter => 1, :c => %w(subject total_spent_hours)
889 get :index, :set_filter => 1, :c => %w(subject total_spent_hours)
890 assert_select 'table.issues tr#issue-3 td.total_spent_hours', :text => '1.00'
890 assert_select 'table.issues tr#issue-3 td.total_spent_hours', :text => '1.00'
891 end
891 end
892
892
893 def test_index_with_total_estimated_hours_column
893 def test_index_with_total_estimated_hours_column
894 get :index, :set_filter => 1, :c => %w(subject total_estimated_hours)
894 get :index, :set_filter => 1, :c => %w(subject total_estimated_hours)
895 assert_select 'table.issues td.total_estimated_hours'
895 assert_select 'table.issues td.total_estimated_hours'
896 end
896 end
897
897
898 def test_index_should_not_show_spent_hours_column_without_permission
898 def test_index_should_not_show_spent_hours_column_without_permission
899 Role.anonymous.remove_permission! :view_time_entries
899 Role.anonymous.remove_permission! :view_time_entries
900 get :index, :set_filter => 1, :c => %w(subject spent_hours)
900 get :index, :set_filter => 1, :c => %w(subject spent_hours)
901 assert_select 'td.spent_hours', 0
901 assert_select 'td.spent_hours', 0
902 end
902 end
903
903
904 def test_index_with_fixed_version_column
904 def test_index_with_fixed_version_column
905 get :index, :set_filter => 1, :c => %w(fixed_version)
905 get :index, :set_filter => 1, :c => %w(fixed_version)
906 assert_select 'table.issues td.fixed_version' do
906 assert_select 'table.issues td.fixed_version' do
907 assert_select 'a[href=?]', '/versions/2', :text => 'eCookbook - 1.0'
907 assert_select 'a[href=?]', '/versions/2', :text => 'eCookbook - 1.0'
908 end
908 end
909 end
909 end
910
910
911 def test_index_with_relations_column
911 def test_index_with_relations_column
912 IssueRelation.delete_all
912 IssueRelation.delete_all
913 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(7))
913 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(7))
914 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(8), :issue_to => Issue.find(1))
914 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(8), :issue_to => Issue.find(1))
915 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(1), :issue_to => Issue.find(11))
915 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(1), :issue_to => Issue.find(11))
916 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(12), :issue_to => Issue.find(2))
916 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(12), :issue_to => Issue.find(2))
917
917
918 get :index, :set_filter => 1, :c => %w(subject relations)
918 get :index, :set_filter => 1, :c => %w(subject relations)
919 assert_response :success
919 assert_response :success
920 assert_select "tr#issue-1 td.relations" do
920 assert_select "tr#issue-1 td.relations" do
921 assert_select "span", 3
921 assert_select "span", 3
922 assert_select "span", :text => "Related to #7"
922 assert_select "span", :text => "Related to #7"
923 assert_select "span", :text => "Related to #8"
923 assert_select "span", :text => "Related to #8"
924 assert_select "span", :text => "Blocks #11"
924 assert_select "span", :text => "Blocks #11"
925 end
925 end
926 assert_select "tr#issue-2 td.relations" do
926 assert_select "tr#issue-2 td.relations" do
927 assert_select "span", 1
927 assert_select "span", 1
928 assert_select "span", :text => "Blocked by #12"
928 assert_select "span", :text => "Blocked by #12"
929 end
929 end
930 assert_select "tr#issue-3 td.relations" do
930 assert_select "tr#issue-3 td.relations" do
931 assert_select "span", 0
931 assert_select "span", 0
932 end
932 end
933
933
934 get :index, :set_filter => 1, :c => %w(relations), :format => 'csv'
934 get :index, :set_filter => 1, :c => %w(relations), :format => 'csv'
935 assert_response :success
935 assert_response :success
936 assert_equal 'text/csv; header=present', response.content_type
936 assert_equal 'text/csv; header=present', response.content_type
937 lines = response.body.chomp.split("\n")
937 lines = response.body.chomp.split("\n")
938 assert_include '1,"Related to #7, Related to #8, Blocks #11"', lines
938 assert_include '1,"Related to #7, Related to #8, Blocks #11"', lines
939 assert_include '2,Blocked by #12', lines
939 assert_include '2,Blocked by #12', lines
940 assert_include '3,""', lines
940 assert_include '3,""', lines
941
941
942 get :index, :set_filter => 1, :c => %w(subject relations), :format => 'pdf'
942 get :index, :set_filter => 1, :c => %w(subject relations), :format => 'pdf'
943 assert_response :success
943 assert_response :success
944 assert_equal 'application/pdf', response.content_type
944 assert_equal 'application/pdf', response.content_type
945 end
945 end
946
946
947 def test_index_with_description_column
947 def test_index_with_description_column
948 get :index, :set_filter => 1, :c => %w(subject description)
948 get :index, :set_filter => 1, :c => %w(subject description)
949
949
950 assert_select 'table.issues thead th', 3 # columns: chekbox + id + subject
950 assert_select 'table.issues thead th', 3 # columns: chekbox + id + subject
951 assert_select 'td.description[colspan="3"]', :text => 'Unable to print recipes'
951 assert_select 'td.description[colspan="3"]', :text => 'Unable to print recipes'
952
952
953 get :index, :set_filter => 1, :c => %w(subject description), :format => 'pdf'
953 get :index, :set_filter => 1, :c => %w(subject description), :format => 'pdf'
954 assert_response :success
954 assert_response :success
955 assert_equal 'application/pdf', response.content_type
955 assert_equal 'application/pdf', response.content_type
956 end
956 end
957
957
958 def test_index_with_parent_column
958 def test_index_with_parent_column
959 Issue.delete_all
959 Issue.delete_all
960 parent = Issue.generate!
960 parent = Issue.generate!
961 child = Issue.generate!(:parent_issue_id => parent.id)
961 child = Issue.generate!(:parent_issue_id => parent.id)
962
962
963 get :index, :c => %w(parent)
963 get :index, :c => %w(parent)
964
964
965 assert_select 'td.parent', :text => "#{parent.tracker} ##{parent.id}"
965 assert_select 'td.parent', :text => "#{parent.tracker} ##{parent.id}"
966 assert_select 'td.parent a[title=?]', parent.subject
966 assert_select 'td.parent a[title=?]', parent.subject
967 end
967 end
968
968
969 def test_index_with_estimated_hours_total
969 def test_index_with_estimated_hours_total
970 Issue.delete_all
970 Issue.delete_all
971 Issue.generate!(:estimated_hours => 5.5)
971 Issue.generate!(:estimated_hours => 5.5)
972 Issue.generate!(:estimated_hours => 1.1)
972 Issue.generate!(:estimated_hours => 1.1)
973
973
974 get :index, :t => %w(estimated_hours)
974 get :index, :t => %w(estimated_hours)
975 assert_response :success
975 assert_response :success
976 assert_select '.query-totals'
976 assert_select '.query-totals'
977 assert_select '.total-for-estimated-hours span.value', :text => '6.60'
977 assert_select '.total-for-estimated-hours span.value', :text => '6.60'
978 assert_select 'input[type=checkbox][name=?][value=estimated_hours][checked=checked]', 't[]'
978 assert_select 'input[type=checkbox][name=?][value=estimated_hours][checked=checked]', 't[]'
979 end
979 end
980
980
981 def test_index_with_grouped_query_and_estimated_hours_total
981 def test_index_with_grouped_query_and_estimated_hours_total
982 Issue.delete_all
982 Issue.delete_all
983 Issue.generate!(:estimated_hours => 5.5, :category_id => 1)
983 Issue.generate!(:estimated_hours => 5.5, :category_id => 1)
984 Issue.generate!(:estimated_hours => 2.3, :category_id => 1)
984 Issue.generate!(:estimated_hours => 2.3, :category_id => 1)
985 Issue.generate!(:estimated_hours => 1.1, :category_id => 2)
985 Issue.generate!(:estimated_hours => 1.1, :category_id => 2)
986 Issue.generate!(:estimated_hours => 4.6)
986 Issue.generate!(:estimated_hours => 4.6)
987
987
988 get :index, :t => %w(estimated_hours), :group_by => 'category'
988 get :index, :t => %w(estimated_hours), :group_by => 'category'
989 assert_response :success
989 assert_response :success
990 assert_select '.query-totals'
990 assert_select '.query-totals'
991 assert_select '.query-totals .total-for-estimated-hours span.value', :text => '13.50'
991 assert_select '.query-totals .total-for-estimated-hours span.value', :text => '13.50'
992 assert_select 'tr.group', :text => /Printing/ do
992 assert_select 'tr.group', :text => /Printing/ do
993 assert_select '.total-for-estimated-hours span.value', :text => '7.80'
993 assert_select '.total-for-estimated-hours span.value', :text => '7.80'
994 end
994 end
995 assert_select 'tr.group', :text => /Recipes/ do
995 assert_select 'tr.group', :text => /Recipes/ do
996 assert_select '.total-for-estimated-hours span.value', :text => '1.10'
996 assert_select '.total-for-estimated-hours span.value', :text => '1.10'
997 end
997 end
998 assert_select 'tr.group', :text => /blank/ do
998 assert_select 'tr.group', :text => /blank/ do
999 assert_select '.total-for-estimated-hours span.value', :text => '4.60'
999 assert_select '.total-for-estimated-hours span.value', :text => '4.60'
1000 end
1000 end
1001 end
1001 end
1002
1002
1003 def test_index_with_int_custom_field_total
1003 def test_index_with_int_custom_field_total
1004 field = IssueCustomField.generate!(:field_format => 'int', :is_for_all => true)
1004 field = IssueCustomField.generate!(:field_format => 'int', :is_for_all => true)
1005 CustomValue.create!(:customized => Issue.find(1), :custom_field => field, :value => '2')
1005 CustomValue.create!(:customized => Issue.find(1), :custom_field => field, :value => '2')
1006 CustomValue.create!(:customized => Issue.find(2), :custom_field => field, :value => '7')
1006 CustomValue.create!(:customized => Issue.find(2), :custom_field => field, :value => '7')
1007
1007
1008 get :index, :t => ["cf_#{field.id}"]
1008 get :index, :t => ["cf_#{field.id}"]
1009 assert_response :success
1009 assert_response :success
1010 assert_select '.query-totals'
1010 assert_select '.query-totals'
1011 assert_select ".total-for-cf-#{field.id} span.value", :text => '9'
1011 assert_select ".total-for-cf-#{field.id} span.value", :text => '9'
1012 end
1012 end
1013
1013
1014 def test_index_totals_should_default_to_settings
1014 def test_index_totals_should_default_to_settings
1015 with_settings :issue_list_default_totals => ['estimated_hours'] do
1015 with_settings :issue_list_default_totals => ['estimated_hours'] do
1016 get :index
1016 get :index
1017 assert_response :success
1017 assert_response :success
1018 assert_select '.total-for-estimated-hours span.value'
1018 assert_select '.total-for-estimated-hours span.value'
1019 assert_select '.query-totals>span', 1
1019 assert_select '.query-totals>span', 1
1020 end
1020 end
1021 end
1021 end
1022
1022
1023 def test_index_send_html_if_query_is_invalid
1023 def test_index_send_html_if_query_is_invalid
1024 get :index, :f => ['start_date'], :op => {:start_date => '='}
1024 get :index, :f => ['start_date'], :op => {:start_date => '='}
1025 assert_equal 'text/html', @response.content_type
1025 assert_equal 'text/html', @response.content_type
1026 assert_select_error /Start date cannot be blank/i
1026 assert_select_error /Start date cannot be blank/i
1027 end
1027 end
1028
1028
1029 def test_index_send_nothing_if_query_is_invalid
1029 def test_index_send_nothing_if_query_is_invalid
1030 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
1030 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
1031 assert_equal 'text/csv', @response.content_type
1031 assert_equal 'text/csv', @response.content_type
1032 assert @response.body.blank?
1032 assert @response.body.blank?
1033 end
1033 end
1034
1034
1035 def test_index_should_include_new_issue_link
1035 def test_index_should_include_new_issue_link
1036 @request.session[:user_id] = 2
1036 @request.session[:user_id] = 2
1037 get :index, :project_id => 1
1037 get :index, :project_id => 1
1038 assert_select '#content a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
1038 assert_select '#content a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
1039 end
1039 end
1040
1040
1041 def test_index_should_not_include_new_issue_link_for_project_without_trackers
1041 def test_index_should_not_include_new_issue_link_for_project_without_trackers
1042 Project.find(1).trackers.clear
1042 Project.find(1).trackers.clear
1043
1043
1044 @request.session[:user_id] = 2
1044 @request.session[:user_id] = 2
1045 get :index, :project_id => 1
1045 get :index, :project_id => 1
1046 assert_select '#content a.new-issue', 0
1046 assert_select '#content a.new-issue', 0
1047 end
1047 end
1048
1048
1049 def test_index_should_not_include_new_issue_link_for_users_with_copy_issues_permission_only
1049 def test_index_should_not_include_new_issue_link_for_users_with_copy_issues_permission_only
1050 role = Role.find(1)
1050 role = Role.find(1)
1051 role.remove_permission! :add_issues
1051 role.remove_permission! :add_issues
1052 role.add_permission! :copy_issues
1052 role.add_permission! :copy_issues
1053
1053
1054 @request.session[:user_id] = 2
1054 @request.session[:user_id] = 2
1055 get :index, :project_id => 1
1055 get :index, :project_id => 1
1056 assert_select '#content a.new-issue', 0
1056 assert_select '#content a.new-issue', 0
1057 end
1057 end
1058
1058
1059 def test_index_without_project_should_include_new_issue_link
1059 def test_index_without_project_should_include_new_issue_link
1060 @request.session[:user_id] = 2
1060 @request.session[:user_id] = 2
1061 get :index
1061 get :index
1062 assert_select '#content a.new-issue[href="/issues/new"]', :text => 'New issue'
1062 assert_select '#content a.new-issue[href="/issues/new"]', :text => 'New issue'
1063 end
1063 end
1064
1064
1065 def test_index_should_not_include_new_issue_tab_when_disabled
1065 def test_index_should_not_include_new_issue_tab_when_disabled
1066 with_settings :new_item_menu_tab => '0' do
1066 with_settings :new_item_menu_tab => '0' do
1067 @request.session[:user_id] = 2
1067 @request.session[:user_id] = 2
1068 get :index, :project_id => 1
1068 get :index, :project_id => 1
1069 assert_select '#main-menu a.new-issue', 0
1069 assert_select '#main-menu a.new-issue', 0
1070 end
1070 end
1071 end
1071 end
1072
1072
1073 def test_index_should_include_new_issue_tab_when_enabled
1073 def test_index_should_include_new_issue_tab_when_enabled
1074 with_settings :new_item_menu_tab => '1' do
1074 with_settings :new_item_menu_tab => '1' do
1075 @request.session[:user_id] = 2
1075 @request.session[:user_id] = 2
1076 get :index, :project_id => 1
1076 get :index, :project_id => 1
1077 assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
1077 assert_select '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]', :text => 'New issue'
1078 end
1078 end
1079 end
1079 end
1080
1080
1081 def test_new_should_have_new_issue_tab_as_current_menu_item
1081 def test_new_should_have_new_issue_tab_as_current_menu_item
1082 with_settings :new_item_menu_tab => '1' do
1082 with_settings :new_item_menu_tab => '1' do
1083 @request.session[:user_id] = 2
1083 @request.session[:user_id] = 2
1084 get :new, :project_id => 1
1084 get :new, :project_id => 1
1085 assert_select '#main-menu a.new-issue.selected'
1085 assert_select '#main-menu a.new-issue.selected'
1086 end
1086 end
1087 end
1087 end
1088
1088
1089 def test_index_should_not_include_new_issue_tab_for_project_without_trackers
1089 def test_index_should_not_include_new_issue_tab_for_project_without_trackers
1090 with_settings :new_item_menu_tab => '1' do
1090 with_settings :new_item_menu_tab => '1' do
1091 Project.find(1).trackers.clear
1091 Project.find(1).trackers.clear
1092
1092
1093 @request.session[:user_id] = 2
1093 @request.session[:user_id] = 2
1094 get :index, :project_id => 1
1094 get :index, :project_id => 1
1095 assert_select '#main-menu a.new-issue', 0
1095 assert_select '#main-menu a.new-issue', 0
1096 end
1096 end
1097 end
1097 end
1098
1098
1099 def test_index_should_not_include_new_issue_tab_for_users_with_copy_issues_permission_only
1099 def test_index_should_not_include_new_issue_tab_for_users_with_copy_issues_permission_only
1100 with_settings :new_item_menu_tab => '1' do
1100 with_settings :new_item_menu_tab => '1' do
1101 role = Role.find(1)
1101 role = Role.find(1)
1102 role.remove_permission! :add_issues
1102 role.remove_permission! :add_issues
1103 role.add_permission! :copy_issues
1103 role.add_permission! :copy_issues
1104
1104
1105 @request.session[:user_id] = 2
1105 @request.session[:user_id] = 2
1106 get :index, :project_id => 1
1106 get :index, :project_id => 1
1107 assert_select '#main-menu a.new-issue', 0
1107 assert_select '#main-menu a.new-issue', 0
1108 end
1108 end
1109 end
1109 end
1110
1110
1111 def test_show_by_anonymous
1111 def test_show_by_anonymous
1112 get :show, :id => 1
1112 get :show, :id => 1
1113 assert_response :success
1113 assert_response :success
1114
1114
1115 assert_select 'div.issue div.description', :text => /Unable to print recipes/
1115 assert_select 'div.issue div.description', :text => /Unable to print recipes/
1116 # anonymous role is allowed to add a note
1116 # anonymous role is allowed to add a note
1117 assert_select 'form#issue-form' do
1117 assert_select 'form#issue-form' do
1118 assert_select 'fieldset' do
1118 assert_select 'fieldset' do
1119 assert_select 'legend', :text => 'Notes'
1119 assert_select 'legend', :text => 'Notes'
1120 assert_select 'textarea[name=?]', 'issue[notes]'
1120 assert_select 'textarea[name=?]', 'issue[notes]'
1121 end
1121 end
1122 end
1122 end
1123 assert_select 'title', :text => "Bug #1: Cannot print recipes - eCookbook - Redmine"
1123 assert_select 'title', :text => "Bug #1: Cannot print recipes - eCookbook - Redmine"
1124 end
1124 end
1125
1125
1126 def test_show_by_manager
1126 def test_show_by_manager
1127 @request.session[:user_id] = 2
1127 @request.session[:user_id] = 2
1128 get :show, :id => 1
1128 get :show, :id => 1
1129
1129
1130 assert_select 'a', :text => /Quote/
1130 assert_select 'a', :text => /Quote/
1131 assert_select 'form#issue-form' do
1131 assert_select 'form#issue-form' do
1132 assert_select 'fieldset' do
1132 assert_select 'fieldset' do
1133 assert_select 'legend', :text => 'Change properties'
1133 assert_select 'legend', :text => 'Change properties'
1134 assert_select 'input[name=?]', 'issue[subject]'
1134 assert_select 'input[name=?]', 'issue[subject]'
1135 end
1135 end
1136 assert_select 'fieldset' do
1136 assert_select 'fieldset' do
1137 assert_select 'legend', :text => 'Log time'
1137 assert_select 'legend', :text => 'Log time'
1138 assert_select 'input[name=?]', 'time_entry[hours]'
1138 assert_select 'input[name=?]', 'time_entry[hours]'
1139 end
1139 end
1140 assert_select 'fieldset' do
1140 assert_select 'fieldset' do
1141 assert_select 'legend', :text => 'Notes'
1141 assert_select 'legend', :text => 'Notes'
1142 assert_select 'textarea[name=?]', 'issue[notes]'
1142 assert_select 'textarea[name=?]', 'issue[notes]'
1143 end
1143 end
1144 end
1144 end
1145 end
1145 end
1146
1146
1147 def test_show_should_display_update_form
1147 def test_show_should_display_update_form
1148 @request.session[:user_id] = 2
1148 @request.session[:user_id] = 2
1149 get :show, :id => 1
1149 get :show, :id => 1
1150 assert_response :success
1150 assert_response :success
1151
1151
1152 assert_select 'form#issue-form' do
1152 assert_select 'form#issue-form' do
1153 assert_select 'input[name=?]', 'issue[is_private]'
1153 assert_select 'input[name=?]', 'issue[is_private]'
1154 assert_select 'select[name=?]', 'issue[project_id]'
1154 assert_select 'select[name=?]', 'issue[project_id]'
1155 assert_select 'select[name=?]', 'issue[tracker_id]'
1155 assert_select 'select[name=?]', 'issue[tracker_id]'
1156 assert_select 'input[name=?]', 'issue[subject]'
1156 assert_select 'input[name=?]', 'issue[subject]'
1157 assert_select 'textarea[name=?]', 'issue[description]'
1157 assert_select 'textarea[name=?]', 'issue[description]'
1158 assert_select 'select[name=?]', 'issue[status_id]'
1158 assert_select 'select[name=?]', 'issue[status_id]'
1159 assert_select 'select[name=?]', 'issue[priority_id]'
1159 assert_select 'select[name=?]', 'issue[priority_id]'
1160 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1160 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1161 assert_select 'select[name=?]', 'issue[category_id]'
1161 assert_select 'select[name=?]', 'issue[category_id]'
1162 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1162 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1163 assert_select 'input[name=?]', 'issue[parent_issue_id]'
1163 assert_select 'input[name=?]', 'issue[parent_issue_id]'
1164 assert_select 'input[name=?]', 'issue[start_date]'
1164 assert_select 'input[name=?]', 'issue[start_date]'
1165 assert_select 'input[name=?]', 'issue[due_date]'
1165 assert_select 'input[name=?]', 'issue[due_date]'
1166 assert_select 'select[name=?]', 'issue[done_ratio]'
1166 assert_select 'select[name=?]', 'issue[done_ratio]'
1167 assert_select 'input[name=?]', 'issue[custom_field_values][2]'
1167 assert_select 'input[name=?]', 'issue[custom_field_values][2]'
1168 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1168 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1169 assert_select 'textarea[name=?]', 'issue[notes]'
1169 assert_select 'textarea[name=?]', 'issue[notes]'
1170 end
1170 end
1171 end
1171 end
1172
1172
1173 def test_show_should_display_update_form_with_minimal_permissions
1173 def test_show_should_display_update_form_with_minimal_permissions
1174 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
1174 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
1175 WorkflowTransition.where(:role_id => 1).delete_all
1175 WorkflowTransition.where(:role_id => 1).delete_all
1176
1176
1177 @request.session[:user_id] = 2
1177 @request.session[:user_id] = 2
1178 get :show, :id => 1
1178 get :show, :id => 1
1179 assert_response :success
1179 assert_response :success
1180
1180
1181 assert_select 'form#issue-form' do
1181 assert_select 'form#issue-form' do
1182 assert_select 'input[name=?]', 'issue[is_private]', 0
1182 assert_select 'input[name=?]', 'issue[is_private]', 0
1183 assert_select 'select[name=?]', 'issue[project_id]', 0
1183 assert_select 'select[name=?]', 'issue[project_id]', 0
1184 assert_select 'select[name=?]', 'issue[tracker_id]', 0
1184 assert_select 'select[name=?]', 'issue[tracker_id]', 0
1185 assert_select 'input[name=?]', 'issue[subject]', 0
1185 assert_select 'input[name=?]', 'issue[subject]', 0
1186 assert_select 'textarea[name=?]', 'issue[description]', 0
1186 assert_select 'textarea[name=?]', 'issue[description]', 0
1187 assert_select 'select[name=?]', 'issue[status_id]', 0
1187 assert_select 'select[name=?]', 'issue[status_id]', 0
1188 assert_select 'select[name=?]', 'issue[priority_id]', 0
1188 assert_select 'select[name=?]', 'issue[priority_id]', 0
1189 assert_select 'select[name=?]', 'issue[assigned_to_id]', 0
1189 assert_select 'select[name=?]', 'issue[assigned_to_id]', 0
1190 assert_select 'select[name=?]', 'issue[category_id]', 0
1190 assert_select 'select[name=?]', 'issue[category_id]', 0
1191 assert_select 'select[name=?]', 'issue[fixed_version_id]', 0
1191 assert_select 'select[name=?]', 'issue[fixed_version_id]', 0
1192 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
1192 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
1193 assert_select 'input[name=?]', 'issue[start_date]', 0
1193 assert_select 'input[name=?]', 'issue[start_date]', 0
1194 assert_select 'input[name=?]', 'issue[due_date]', 0
1194 assert_select 'input[name=?]', 'issue[due_date]', 0
1195 assert_select 'select[name=?]', 'issue[done_ratio]', 0
1195 assert_select 'select[name=?]', 'issue[done_ratio]', 0
1196 assert_select 'input[name=?]', 'issue[custom_field_values][2]', 0
1196 assert_select 'input[name=?]', 'issue[custom_field_values][2]', 0
1197 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1197 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1198 assert_select 'textarea[name=?]', 'issue[notes]'
1198 assert_select 'textarea[name=?]', 'issue[notes]'
1199 end
1199 end
1200 end
1200 end
1201
1201
1202 def test_show_should_not_display_update_form_without_permissions
1202 def test_show_should_not_display_update_form_without_permissions
1203 Role.find(1).update_attribute :permissions, [:view_issues]
1203 Role.find(1).update_attribute :permissions, [:view_issues]
1204
1204
1205 @request.session[:user_id] = 2
1205 @request.session[:user_id] = 2
1206 get :show, :id => 1
1206 get :show, :id => 1
1207 assert_response :success
1207 assert_response :success
1208
1208
1209 assert_select 'form#issue-form', 0
1209 assert_select 'form#issue-form', 0
1210 end
1210 end
1211
1211
1212 def test_update_form_should_not_display_inactive_enumerations
1212 def test_update_form_should_not_display_inactive_enumerations
1213 assert !IssuePriority.find(15).active?
1213 assert !IssuePriority.find(15).active?
1214
1214
1215 @request.session[:user_id] = 2
1215 @request.session[:user_id] = 2
1216 get :show, :id => 1
1216 get :show, :id => 1
1217 assert_response :success
1217 assert_response :success
1218
1218
1219 assert_select 'form#issue-form' do
1219 assert_select 'form#issue-form' do
1220 assert_select 'select[name=?]', 'issue[priority_id]' do
1220 assert_select 'select[name=?]', 'issue[priority_id]' do
1221 assert_select 'option[value="4"]'
1221 assert_select 'option[value="4"]'
1222 assert_select 'option[value="15"]', 0
1222 assert_select 'option[value="15"]', 0
1223 end
1223 end
1224 end
1224 end
1225 end
1225 end
1226
1226
1227 def test_update_form_should_allow_attachment_upload
1227 def test_update_form_should_allow_attachment_upload
1228 @request.session[:user_id] = 2
1228 @request.session[:user_id] = 2
1229 get :show, :id => 1
1229 get :show, :id => 1
1230
1230
1231 assert_select 'form#issue-form[method=post][enctype="multipart/form-data"]' do
1231 assert_select 'form#issue-form[method=post][enctype="multipart/form-data"]' do
1232 assert_select 'input[type=file][name=?]', 'attachments[dummy][file]'
1232 assert_select 'input[type=file][name=?]', 'attachments[dummy][file]'
1233 end
1233 end
1234 end
1234 end
1235
1235
1236 def test_show_should_deny_anonymous_access_without_permission
1236 def test_show_should_deny_anonymous_access_without_permission
1237 Role.anonymous.remove_permission!(:view_issues)
1237 Role.anonymous.remove_permission!(:view_issues)
1238 get :show, :id => 1
1238 get :show, :id => 1
1239 assert_response :redirect
1239 assert_response :redirect
1240 end
1240 end
1241
1241
1242 def test_show_should_deny_anonymous_access_to_private_issue
1242 def test_show_should_deny_anonymous_access_to_private_issue
1243 Issue.where(:id => 1).update_all(["is_private = ?", true])
1243 Issue.where(:id => 1).update_all(["is_private = ?", true])
1244 get :show, :id => 1
1244 get :show, :id => 1
1245 assert_response :redirect
1245 assert_response :redirect
1246 end
1246 end
1247
1247
1248 def test_show_should_deny_non_member_access_without_permission
1248 def test_show_should_deny_non_member_access_without_permission
1249 Role.non_member.remove_permission!(:view_issues)
1249 Role.non_member.remove_permission!(:view_issues)
1250 @request.session[:user_id] = 9
1250 @request.session[:user_id] = 9
1251 get :show, :id => 1
1251 get :show, :id => 1
1252 assert_response 403
1252 assert_response 403
1253 end
1253 end
1254
1254
1255 def test_show_should_deny_non_member_access_to_private_issue
1255 def test_show_should_deny_non_member_access_to_private_issue
1256 Issue.where(:id => 1).update_all(["is_private = ?", true])
1256 Issue.where(:id => 1).update_all(["is_private = ?", true])
1257 @request.session[:user_id] = 9
1257 @request.session[:user_id] = 9
1258 get :show, :id => 1
1258 get :show, :id => 1
1259 assert_response 403
1259 assert_response 403
1260 end
1260 end
1261
1261
1262 def test_show_should_deny_member_access_without_permission
1262 def test_show_should_deny_member_access_without_permission
1263 Role.find(1).remove_permission!(:view_issues)
1263 Role.find(1).remove_permission!(:view_issues)
1264 @request.session[:user_id] = 2
1264 @request.session[:user_id] = 2
1265 get :show, :id => 1
1265 get :show, :id => 1
1266 assert_response 403
1266 assert_response 403
1267 end
1267 end
1268
1268
1269 def test_show_should_deny_member_access_to_private_issue_without_permission
1269 def test_show_should_deny_member_access_to_private_issue_without_permission
1270 Issue.where(:id => 1).update_all(["is_private = ?", true])
1270 Issue.where(:id => 1).update_all(["is_private = ?", true])
1271 @request.session[:user_id] = 3
1271 @request.session[:user_id] = 3
1272 get :show, :id => 1
1272 get :show, :id => 1
1273 assert_response 403
1273 assert_response 403
1274 end
1274 end
1275
1275
1276 def test_show_should_allow_author_access_to_private_issue
1276 def test_show_should_allow_author_access_to_private_issue
1277 Issue.where(:id => 1).update_all(["is_private = ?, author_id = 3", true])
1277 Issue.where(:id => 1).update_all(["is_private = ?, author_id = 3", true])
1278 @request.session[:user_id] = 3
1278 @request.session[:user_id] = 3
1279 get :show, :id => 1
1279 get :show, :id => 1
1280 assert_response :success
1280 assert_response :success
1281 end
1281 end
1282
1282
1283 def test_show_should_allow_assignee_access_to_private_issue
1283 def test_show_should_allow_assignee_access_to_private_issue
1284 Issue.where(:id => 1).update_all(["is_private = ?, assigned_to_id = 3", true])
1284 Issue.where(:id => 1).update_all(["is_private = ?, assigned_to_id = 3", true])
1285 @request.session[:user_id] = 3
1285 @request.session[:user_id] = 3
1286 get :show, :id => 1
1286 get :show, :id => 1
1287 assert_response :success
1287 assert_response :success
1288 end
1288 end
1289
1289
1290 def test_show_should_allow_member_access_to_private_issue_with_permission
1290 def test_show_should_allow_member_access_to_private_issue_with_permission
1291 Issue.where(:id => 1).update_all(["is_private = ?", true])
1291 Issue.where(:id => 1).update_all(["is_private = ?", true])
1292 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
1292 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
1293 @request.session[:user_id] = 3
1293 @request.session[:user_id] = 3
1294 get :show, :id => 1
1294 get :show, :id => 1
1295 assert_response :success
1295 assert_response :success
1296 end
1296 end
1297
1297
1298 def test_show_should_not_disclose_relations_to_invisible_issues
1298 def test_show_should_not_disclose_relations_to_invisible_issues
1299 Setting.cross_project_issue_relations = '1'
1299 Setting.cross_project_issue_relations = '1'
1300 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
1300 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
1301 # Relation to a private project issue
1301 # Relation to a private project issue
1302 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
1302 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
1303
1303
1304 get :show, :id => 1
1304 get :show, :id => 1
1305 assert_response :success
1305 assert_response :success
1306
1306
1307 assert_select 'div#relations' do
1307 assert_select 'div#relations' do
1308 assert_select 'a', :text => /#2$/
1308 assert_select 'a', :text => /#2$/
1309 assert_select 'a', :text => /#4$/, :count => 0
1309 assert_select 'a', :text => /#4$/, :count => 0
1310 end
1310 end
1311 end
1311 end
1312
1312
1313 def test_show_should_list_subtasks
1313 def test_show_should_list_subtasks
1314 Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1314 Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1315
1315
1316 get :show, :id => 1
1316 get :show, :id => 1
1317 assert_response :success
1317 assert_response :success
1318
1318
1319 assert_select 'div#issue_tree' do
1319 assert_select 'div#issue_tree' do
1320 assert_select 'td.subject', :text => /Child Issue/
1320 assert_select 'td.subject', :text => /Child Issue/
1321 end
1321 end
1322 end
1322 end
1323
1323
1324 def test_show_should_list_parents
1324 def test_show_should_list_parents
1325 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1325 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1326
1326
1327 get :show, :id => issue.id
1327 get :show, :id => issue.id
1328 assert_response :success
1328 assert_response :success
1329
1329
1330 assert_select 'div.subject' do
1330 assert_select 'div.subject' do
1331 assert_select 'h3', 'Child Issue'
1331 assert_select 'h3', 'Child Issue'
1332 assert_select 'a[href="/issues/1"]'
1332 assert_select 'a[href="/issues/1"]'
1333 end
1333 end
1334 end
1334 end
1335
1335
1336 def test_show_should_not_display_prev_next_links_without_query_in_session
1336 def test_show_should_not_display_prev_next_links_without_query_in_session
1337 get :show, :id => 1
1337 get :show, :id => 1
1338 assert_response :success
1338 assert_response :success
1339
1339
1340 assert_select 'div.next-prev-links', 0
1340 assert_select 'div.next-prev-links', 0
1341 end
1341 end
1342
1342
1343 def test_show_should_display_prev_next_links_with_query_in_session
1343 def test_show_should_display_prev_next_links_with_query_in_session
1344 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1344 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1345 @request.session['issues_index_sort'] = 'id'
1345 @request.session['issues_index_sort'] = 'id'
1346
1346
1347 with_settings :display_subprojects_issues => '0' do
1347 with_settings :display_subprojects_issues => '0' do
1348 get :show, :id => 3
1348 get :show, :id => 3
1349 end
1349 end
1350 assert_response :success
1350 assert_response :success
1351
1351
1352 count = Issue.open.visible.count
1352 count = Issue.open.visible.count
1353
1353
1354 # Previous and next issues for all projects
1354 # Previous and next issues for all projects
1355 assert_select 'div.next-prev-links' do
1355 assert_select 'div.next-prev-links' do
1356 assert_select 'a[href="/issues/2"]', :text => /Previous/
1356 assert_select 'a[href="/issues/2"]', :text => /Previous/
1357 assert_select 'a[href="/issues/5"]', :text => /Next/
1357 assert_select 'a[href="/issues/5"]', :text => /Next/
1358 assert_select 'span.position', :text => "3 of #{count}"
1358 assert_select 'span.position', :text => "3 of #{count}"
1359 end
1359 end
1360 end
1360 end
1361
1361
1362 def test_show_should_display_prev_next_links_with_saved_query_in_session
1362 def test_show_should_display_prev_next_links_with_saved_query_in_session
1363 query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC, :user_id => 1,
1363 query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC, :user_id => 1,
1364 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
1364 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
1365 :sort_criteria => [['id', 'asc']])
1365 :sort_criteria => [['id', 'asc']])
1366 @request.session[:query] = {:id => query.id, :project_id => nil}
1366 @request.session[:query] = {:id => query.id, :project_id => nil}
1367
1367
1368 get :show, :id => 11
1368 get :show, :id => 11
1369 assert_response :success
1369 assert_response :success
1370
1370
1371 # Previous and next issues for all projects
1371 # Previous and next issues for all projects
1372 assert_select 'div.next-prev-links' do
1372 assert_select 'div.next-prev-links' do
1373 assert_select 'a[href="/issues/8"]', :text => /Previous/
1373 assert_select 'a[href="/issues/8"]', :text => /Previous/
1374 assert_select 'a[href="/issues/12"]', :text => /Next/
1374 assert_select 'a[href="/issues/12"]', :text => /Next/
1375 end
1375 end
1376 end
1376 end
1377
1377
1378 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
1378 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
1379 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1379 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1380
1380
1381 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
1381 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
1382 @request.session['issues_index_sort'] = assoc_sort
1382 @request.session['issues_index_sort'] = assoc_sort
1383
1383
1384 get :show, :id => 3
1384 get :show, :id => 3
1385 assert_response :success, "Wrong response status for #{assoc_sort} sort"
1385 assert_response :success, "Wrong response status for #{assoc_sort} sort"
1386
1386
1387 assert_select 'div.next-prev-links' do
1387 assert_select 'div.next-prev-links' do
1388 assert_select 'a', :text => /(Previous|Next)/
1388 assert_select 'a', :text => /(Previous|Next)/
1389 end
1389 end
1390 end
1390 end
1391 end
1391 end
1392
1392
1393 def test_show_should_display_prev_next_links_with_project_query_in_session
1393 def test_show_should_display_prev_next_links_with_project_query_in_session
1394 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1394 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1395 @request.session['issues_index_sort'] = 'id'
1395 @request.session['issues_index_sort'] = 'id'
1396
1396
1397 with_settings :display_subprojects_issues => '0' do
1397 with_settings :display_subprojects_issues => '0' do
1398 get :show, :id => 3
1398 get :show, :id => 3
1399 end
1399 end
1400 assert_response :success
1400 assert_response :success
1401
1401
1402 # Previous and next issues inside project
1402 # Previous and next issues inside project
1403 assert_select 'div.next-prev-links' do
1403 assert_select 'div.next-prev-links' do
1404 assert_select 'a[href="/issues/2"]', :text => /Previous/
1404 assert_select 'a[href="/issues/2"]', :text => /Previous/
1405 assert_select 'a[href="/issues/7"]', :text => /Next/
1405 assert_select 'a[href="/issues/7"]', :text => /Next/
1406 end
1406 end
1407 end
1407 end
1408
1408
1409 def test_show_should_not_display_prev_link_for_first_issue
1409 def test_show_should_not_display_prev_link_for_first_issue
1410 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1410 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1411 @request.session['issues_index_sort'] = 'id'
1411 @request.session['issues_index_sort'] = 'id'
1412
1412
1413 with_settings :display_subprojects_issues => '0' do
1413 with_settings :display_subprojects_issues => '0' do
1414 get :show, :id => 1
1414 get :show, :id => 1
1415 end
1415 end
1416 assert_response :success
1416 assert_response :success
1417
1417
1418 assert_select 'div.next-prev-links' do
1418 assert_select 'div.next-prev-links' do
1419 assert_select 'a', :text => /Previous/, :count => 0
1419 assert_select 'a', :text => /Previous/, :count => 0
1420 assert_select 'a[href="/issues/2"]', :text => /Next/
1420 assert_select 'a[href="/issues/2"]', :text => /Next/
1421 end
1421 end
1422 end
1422 end
1423
1423
1424 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1424 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1425 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1425 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1426 @request.session['issues_index_sort'] = 'id'
1426 @request.session['issues_index_sort'] = 'id'
1427
1427
1428 get :show, :id => 1
1428 get :show, :id => 1
1429 assert_response :success
1429 assert_response :success
1430
1430
1431 assert_select 'a', :text => /Previous/, :count => 0
1431 assert_select 'a', :text => /Previous/, :count => 0
1432 assert_select 'a', :text => /Next/, :count => 0
1432 assert_select 'a', :text => /Next/, :count => 0
1433 end
1433 end
1434
1434
1435 def test_show_show_should_display_prev_next_links_with_query_sort_by_user_custom_field
1435 def test_show_show_should_display_prev_next_links_with_query_sort_by_user_custom_field
1436 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
1436 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
1437 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
1437 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
1438 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
1438 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
1439 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
1439 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
1440 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
1440 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
1441
1441
1442 query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC, :user_id => 1, :filters => {},
1442 query = IssueQuery.create!(:name => 'test', :visibility => IssueQuery::VISIBILITY_PUBLIC, :user_id => 1, :filters => {},
1443 :sort_criteria => [["cf_#{cf.id}", 'asc'], ['id', 'asc']])
1443 :sort_criteria => [["cf_#{cf.id}", 'asc'], ['id', 'asc']])
1444 @request.session[:query] = {:id => query.id, :project_id => nil}
1444 @request.session[:query] = {:id => query.id, :project_id => nil}
1445
1445
1446 get :show, :id => 3
1446 get :show, :id => 3
1447 assert_response :success
1447 assert_response :success
1448
1448
1449 assert_select 'div.next-prev-links' do
1449 assert_select 'div.next-prev-links' do
1450 assert_select 'a[href="/issues/2"]', :text => /Previous/
1450 assert_select 'a[href="/issues/2"]', :text => /Previous/
1451 assert_select 'a[href="/issues/1"]', :text => /Next/
1451 assert_select 'a[href="/issues/1"]', :text => /Next/
1452 end
1452 end
1453 end
1453 end
1454
1454
1455 def test_show_should_display_prev_next_links_when_request_has_previous_and_next_issue_ids_params
1455 def test_show_should_display_prev_next_links_when_request_has_previous_and_next_issue_ids_params
1456 get :show, :id => 1, :prev_issue_id => 1, :next_issue_id => 3, :issue_position => 2, :issue_count => 4
1456 get :show, :id => 1, :prev_issue_id => 1, :next_issue_id => 3, :issue_position => 2, :issue_count => 4
1457 assert_response :success
1457 assert_response :success
1458
1458
1459 assert_select 'div.next-prev-links' do
1459 assert_select 'div.next-prev-links' do
1460 assert_select 'a[href="/issues/1"]', :text => /Previous/
1460 assert_select 'a[href="/issues/1"]', :text => /Previous/
1461 assert_select 'a[href="/issues/3"]', :text => /Next/
1461 assert_select 'a[href="/issues/3"]', :text => /Next/
1462 assert_select 'span.position', :text => "2 of 4"
1462 assert_select 'span.position', :text => "2 of 4"
1463 end
1463 end
1464 end
1464 end
1465
1465
1466 def test_show_should_display_category_field_if_categories_are_defined
1466 def test_show_should_display_category_field_if_categories_are_defined
1467 Issue.update_all :category_id => nil
1467 Issue.update_all :category_id => nil
1468
1468
1469 get :show, :id => 1
1469 get :show, :id => 1
1470 assert_response :success
1470 assert_response :success
1471 assert_select '.attributes .category'
1471 assert_select '.attributes .category'
1472 end
1472 end
1473
1473
1474 def test_show_should_not_display_category_field_if_no_categories_are_defined
1474 def test_show_should_not_display_category_field_if_no_categories_are_defined
1475 Project.find(1).issue_categories.delete_all
1475 Project.find(1).issue_categories.delete_all
1476
1476
1477 get :show, :id => 1
1477 get :show, :id => 1
1478 assert_response :success
1478 assert_response :success
1479 assert_select 'table.attributes .category', 0
1479 assert_select 'table.attributes .category', 0
1480 end
1480 end
1481
1481
1482 def test_show_should_display_link_to_the_assignee
1482 def test_show_should_display_link_to_the_assignee
1483 get :show, :id => 2
1483 get :show, :id => 2
1484 assert_response :success
1484 assert_response :success
1485 assert_select '.assigned-to' do
1485 assert_select '.assigned-to' do
1486 assert_select 'a[href="/users/3"]'
1486 assert_select 'a[href="/users/3"]'
1487 end
1487 end
1488 end
1488 end
1489
1489
1490 def test_show_should_display_visible_changesets_from_other_projects
1490 def test_show_should_display_visible_changesets_from_other_projects
1491 project = Project.find(2)
1491 project = Project.find(2)
1492 issue = project.issues.first
1492 issue = project.issues.first
1493 issue.changeset_ids = [102]
1493 issue.changeset_ids = [102]
1494 issue.save!
1494 issue.save!
1495 # changesets from other projects should be displayed even if repository
1495 # changesets from other projects should be displayed even if repository
1496 # is disabled on issue's project
1496 # is disabled on issue's project
1497 project.disable_module! :repository
1497 project.disable_module! :repository
1498
1498
1499 @request.session[:user_id] = 2
1499 @request.session[:user_id] = 2
1500 get :show, :id => issue.id
1500 get :show, :id => issue.id
1501
1501
1502 assert_select 'a[href=?]', '/projects/ecookbook/repository/revisions/3'
1502 assert_select 'a[href=?]', '/projects/ecookbook/repository/revisions/3'
1503 end
1503 end
1504
1504
1505 def test_show_should_display_watchers
1505 def test_show_should_display_watchers
1506 @request.session[:user_id] = 2
1506 @request.session[:user_id] = 2
1507 Issue.find(1).add_watcher User.find(2)
1507 Issue.find(1).add_watcher User.find(2)
1508
1508
1509 get :show, :id => 1
1509 get :show, :id => 1
1510 assert_select 'div#watchers ul' do
1510 assert_select 'div#watchers ul' do
1511 assert_select 'li' do
1511 assert_select 'li' do
1512 assert_select 'a[href="/users/2"]'
1512 assert_select 'a[href="/users/2"]'
1513 assert_select 'a[class*=delete]'
1513 assert_select 'a[class*=delete]'
1514 end
1514 end
1515 end
1515 end
1516 end
1516 end
1517
1517
1518 def test_show_should_display_watchers_with_gravatars
1518 def test_show_should_display_watchers_with_gravatars
1519 @request.session[:user_id] = 2
1519 @request.session[:user_id] = 2
1520 Issue.find(1).add_watcher User.find(2)
1520 Issue.find(1).add_watcher User.find(2)
1521
1521
1522 with_settings :gravatar_enabled => '1' do
1522 with_settings :gravatar_enabled => '1' do
1523 get :show, :id => 1
1523 get :show, :id => 1
1524 end
1524 end
1525
1525
1526 assert_select 'div#watchers ul' do
1526 assert_select 'div#watchers ul' do
1527 assert_select 'li' do
1527 assert_select 'li' do
1528 assert_select 'img.gravatar'
1528 assert_select 'img.gravatar'
1529 assert_select 'a[href="/users/2"]'
1529 assert_select 'a[href="/users/2"]'
1530 assert_select 'a[class*=delete]'
1530 assert_select 'a[class*=delete]'
1531 end
1531 end
1532 end
1532 end
1533 end
1533 end
1534
1534
1535 def test_show_with_thumbnails_enabled_should_display_thumbnails
1535 def test_show_with_thumbnails_enabled_should_display_thumbnails
1536 @request.session[:user_id] = 2
1536 @request.session[:user_id] = 2
1537
1537
1538 with_settings :thumbnails_enabled => '1' do
1538 with_settings :thumbnails_enabled => '1' do
1539 get :show, :id => 14
1539 get :show, :id => 14
1540 assert_response :success
1540 assert_response :success
1541 end
1541 end
1542
1542
1543 assert_select 'div.thumbnails' do
1543 assert_select 'div.thumbnails' do
1544 assert_select 'a[href="/attachments/16/testfile.png"]' do
1544 assert_select 'a[href="/attachments/16/testfile.png"]' do
1545 assert_select 'img[src="/attachments/thumbnail/16"]'
1545 assert_select 'img[src="/attachments/thumbnail/16"]'
1546 end
1546 end
1547 end
1547 end
1548 end
1548 end
1549
1549
1550 def test_show_with_thumbnails_disabled_should_not_display_thumbnails
1550 def test_show_with_thumbnails_disabled_should_not_display_thumbnails
1551 @request.session[:user_id] = 2
1551 @request.session[:user_id] = 2
1552
1552
1553 with_settings :thumbnails_enabled => '0' do
1553 with_settings :thumbnails_enabled => '0' do
1554 get :show, :id => 14
1554 get :show, :id => 14
1555 assert_response :success
1555 assert_response :success
1556 end
1556 end
1557
1557
1558 assert_select 'div.thumbnails', 0
1558 assert_select 'div.thumbnails', 0
1559 end
1559 end
1560
1560
1561 def test_show_with_multi_custom_field
1561 def test_show_with_multi_custom_field
1562 field = CustomField.find(1)
1562 field = CustomField.find(1)
1563 field.update_attribute :multiple, true
1563 field.update_attribute :multiple, true
1564 issue = Issue.find(1)
1564 issue = Issue.find(1)
1565 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1565 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1566 issue.save!
1566 issue.save!
1567
1567
1568 get :show, :id => 1
1568 get :show, :id => 1
1569 assert_response :success
1569 assert_response :success
1570
1570
1571 assert_select ".cf_1 .value", :text => 'MySQL, Oracle'
1571 assert_select ".cf_1 .value", :text => 'MySQL, Oracle'
1572 end
1572 end
1573
1573
1574 def test_show_with_multi_user_custom_field
1574 def test_show_with_multi_user_custom_field
1575 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1575 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1576 :tracker_ids => [1], :is_for_all => true)
1576 :tracker_ids => [1], :is_for_all => true)
1577 issue = Issue.find(1)
1577 issue = Issue.find(1)
1578 issue.custom_field_values = {field.id => ['2', '3']}
1578 issue.custom_field_values = {field.id => ['2', '3']}
1579 issue.save!
1579 issue.save!
1580
1580
1581 get :show, :id => 1
1581 get :show, :id => 1
1582 assert_response :success
1582 assert_response :success
1583
1583
1584 assert_select ".cf_#{field.id} .value", :text => 'Dave Lopper, John Smith' do
1584 assert_select ".cf_#{field.id} .value", :text => 'Dave Lopper, John Smith' do
1585 assert_select 'a', :text => 'Dave Lopper'
1585 assert_select 'a', :text => 'Dave Lopper'
1586 assert_select 'a', :text => 'John Smith'
1586 assert_select 'a', :text => 'John Smith'
1587 end
1587 end
1588 end
1588 end
1589
1589
1590 def test_show_should_display_private_notes_with_permission_only
1590 def test_show_should_display_private_notes_with_permission_only
1591 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1)
1591 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1)
1592 @request.session[:user_id] = 2
1592 @request.session[:user_id] = 2
1593
1593
1594 get :show, :id => 2
1594 get :show, :id => 2
1595 assert_response :success
1595 assert_response :success
1596 assert_select "#change-#{journal.id}", 1
1596 assert_select "#change-#{journal.id}", 1
1597
1597
1598 Role.find(1).remove_permission! :view_private_notes
1598 Role.find(1).remove_permission! :view_private_notes
1599 get :show, :id => 2
1599 get :show, :id => 2
1600 assert_response :success
1600 assert_response :success
1601 assert_select "#change-#{journal.id}", 0
1601 assert_select "#change-#{journal.id}", 0
1602 end
1602 end
1603
1603
1604 def test_show_atom
1604 def test_show_atom
1605 get :show, :id => 2, :format => 'atom'
1605 get :show, :id => 2, :format => 'atom'
1606 assert_response :success
1606 assert_response :success
1607 assert_equal 'application/atom+xml', response.content_type
1607 assert_equal 'application/atom+xml', response.content_type
1608 # Inline image
1608 # Inline image
1609 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1609 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1610 end
1610 end
1611
1611
1612 def test_show_export_to_pdf
1612 def test_show_export_to_pdf
1613 issue = Issue.find(3)
1613 issue = Issue.find(3)
1614 assert issue.relations.select{|r| r.other_issue(issue).visible?}.present?
1614 assert issue.relations.select{|r| r.other_issue(issue).visible?}.present?
1615 get :show, :id => 3, :format => 'pdf'
1615 get :show, :id => 3, :format => 'pdf'
1616 assert_response :success
1616 assert_response :success
1617 assert_equal 'application/pdf', @response.content_type
1617 assert_equal 'application/pdf', @response.content_type
1618 assert @response.body.starts_with?('%PDF')
1618 assert @response.body.starts_with?('%PDF')
1619 end
1619 end
1620
1620
1621 def test_export_to_pdf_with_utf8_u_fffd
1621 def test_export_to_pdf_with_utf8_u_fffd
1622 # U+FFFD
1622 # U+FFFD
1623 s = "\xef\xbf\xbd"
1623 s = "\xef\xbf\xbd"
1624 s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
1624 s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
1625 issue = Issue.generate!(:subject => s)
1625 issue = Issue.generate!(:subject => s)
1626 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
1626 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
1627 with_settings :default_language => lang do
1627 with_settings :default_language => lang do
1628 get :show, :id => issue.id, :format => 'pdf'
1628 get :show, :id => issue.id, :format => 'pdf'
1629 assert_response :success
1629 assert_response :success
1630 assert_equal 'application/pdf', @response.content_type
1630 assert_equal 'application/pdf', @response.content_type
1631 assert @response.body.starts_with?('%PDF')
1631 assert @response.body.starts_with?('%PDF')
1632 end
1632 end
1633 end
1633 end
1634 end
1634 end
1635
1635
1636 def test_show_export_to_pdf_with_ancestors
1636 def test_show_export_to_pdf_with_ancestors
1637 issue = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1637 issue = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1638
1638
1639 get :show, :id => issue.id, :format => 'pdf'
1639 get :show, :id => issue.id, :format => 'pdf'
1640 assert_response :success
1640 assert_response :success
1641 assert_equal 'application/pdf', @response.content_type
1641 assert_equal 'application/pdf', @response.content_type
1642 assert @response.body.starts_with?('%PDF')
1642 assert @response.body.starts_with?('%PDF')
1643 end
1643 end
1644
1644
1645 def test_show_export_to_pdf_with_descendants
1645 def test_show_export_to_pdf_with_descendants
1646 c1 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1646 c1 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1647 c2 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1647 c2 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1648 c3 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => c1.id)
1648 c3 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => c1.id)
1649
1649
1650 get :show, :id => 1, :format => 'pdf'
1650 get :show, :id => 1, :format => 'pdf'
1651 assert_response :success
1651 assert_response :success
1652 assert_equal 'application/pdf', @response.content_type
1652 assert_equal 'application/pdf', @response.content_type
1653 assert @response.body.starts_with?('%PDF')
1653 assert @response.body.starts_with?('%PDF')
1654 end
1654 end
1655
1655
1656 def test_show_export_to_pdf_with_journals
1656 def test_show_export_to_pdf_with_journals
1657 get :show, :id => 1, :format => 'pdf'
1657 get :show, :id => 1, :format => 'pdf'
1658 assert_response :success
1658 assert_response :success
1659 assert_equal 'application/pdf', @response.content_type
1659 assert_equal 'application/pdf', @response.content_type
1660 assert @response.body.starts_with?('%PDF')
1660 assert @response.body.starts_with?('%PDF')
1661 end
1661 end
1662
1662
1663 def test_show_export_to_pdf_with_changesets
1663 def test_show_export_to_pdf_with_changesets
1664 [[100], [100, 101], [100, 101, 102]].each do |cs|
1664 [[100], [100, 101], [100, 101, 102]].each do |cs|
1665 issue1 = Issue.find(3)
1665 issue1 = Issue.find(3)
1666 issue1.changesets = Changeset.find(cs)
1666 issue1.changesets = Changeset.find(cs)
1667 issue1.save!
1667 issue1.save!
1668 issue = Issue.find(3)
1668 issue = Issue.find(3)
1669 assert_equal issue.changesets.count, cs.size
1669 assert_equal issue.changesets.count, cs.size
1670 get :show, :id => 3, :format => 'pdf'
1670 get :show, :id => 3, :format => 'pdf'
1671 assert_response :success
1671 assert_response :success
1672 assert_equal 'application/pdf', @response.content_type
1672 assert_equal 'application/pdf', @response.content_type
1673 assert @response.body.starts_with?('%PDF')
1673 assert @response.body.starts_with?('%PDF')
1674 end
1674 end
1675 end
1675 end
1676
1676
1677 def test_show_invalid_should_respond_with_404
1677 def test_show_invalid_should_respond_with_404
1678 get :show, :id => 999
1678 get :show, :id => 999
1679 assert_response 404
1679 assert_response 404
1680 end
1680 end
1681
1681
1682 def test_get_new
1682 def test_get_new
1683 @request.session[:user_id] = 2
1683 @request.session[:user_id] = 2
1684 get :new, :project_id => 1, :tracker_id => 1
1684 get :new, :project_id => 1, :tracker_id => 1
1685 assert_response :success
1685 assert_response :success
1686
1686
1687 assert_select 'form#issue-form[action=?]', '/projects/ecookbook/issues'
1687 assert_select 'form#issue-form[action=?]', '/projects/ecookbook/issues'
1688 assert_select 'form#issue-form' do
1688 assert_select 'form#issue-form' do
1689 assert_select 'input[name=?]', 'issue[is_private]'
1689 assert_select 'input[name=?]', 'issue[is_private]'
1690 assert_select 'select[name=?]', 'issue[project_id]', 0
1690 assert_select 'select[name=?]', 'issue[project_id]', 0
1691 assert_select 'select[name=?]', 'issue[tracker_id]'
1691 assert_select 'select[name=?]', 'issue[tracker_id]'
1692 assert_select 'input[name=?]', 'issue[subject]'
1692 assert_select 'input[name=?]', 'issue[subject]'
1693 assert_select 'textarea[name=?]', 'issue[description]'
1693 assert_select 'textarea[name=?]', 'issue[description]'
1694 assert_select 'select[name=?]', 'issue[status_id]'
1694 assert_select 'select[name=?]', 'issue[status_id]'
1695 assert_select 'select[name=?]', 'issue[priority_id]'
1695 assert_select 'select[name=?]', 'issue[priority_id]'
1696 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1696 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1697 assert_select 'select[name=?]', 'issue[category_id]'
1697 assert_select 'select[name=?]', 'issue[category_id]'
1698 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1698 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1699 assert_select 'input[name=?]', 'issue[parent_issue_id]'
1699 assert_select 'input[name=?]', 'issue[parent_issue_id]'
1700 assert_select 'input[name=?]', 'issue[start_date]'
1700 assert_select 'input[name=?]', 'issue[start_date]'
1701 assert_select 'input[name=?]', 'issue[due_date]'
1701 assert_select 'input[name=?]', 'issue[due_date]'
1702 assert_select 'select[name=?]', 'issue[done_ratio]'
1702 assert_select 'select[name=?]', 'issue[done_ratio]'
1703 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
1703 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
1704 assert_select 'input[name=?]', 'issue[watcher_user_ids][]'
1704 assert_select 'input[name=?]', 'issue[watcher_user_ids][]'
1705 end
1705 end
1706
1706
1707 # Be sure we don't display inactive IssuePriorities
1707 # Be sure we don't display inactive IssuePriorities
1708 assert ! IssuePriority.find(15).active?
1708 assert ! IssuePriority.find(15).active?
1709 assert_select 'select[name=?]', 'issue[priority_id]' do
1709 assert_select 'select[name=?]', 'issue[priority_id]' do
1710 assert_select 'option[value="15"]', 0
1710 assert_select 'option[value="15"]', 0
1711 end
1711 end
1712 end
1712 end
1713
1713
1714 def test_get_new_with_minimal_permissions
1714 def test_get_new_with_minimal_permissions
1715 Role.find(1).update_attribute :permissions, [:add_issues]
1715 Role.find(1).update_attribute :permissions, [:add_issues]
1716 WorkflowTransition.where(:role_id => 1).delete_all
1716 WorkflowTransition.where(:role_id => 1).delete_all
1717
1717
1718 @request.session[:user_id] = 2
1718 @request.session[:user_id] = 2
1719 get :new, :project_id => 1, :tracker_id => 1
1719 get :new, :project_id => 1, :tracker_id => 1
1720 assert_response :success
1720 assert_response :success
1721
1721
1722 assert_select 'form#issue-form' do
1722 assert_select 'form#issue-form' do
1723 assert_select 'input[name=?]', 'issue[is_private]', 0
1723 assert_select 'input[name=?]', 'issue[is_private]', 0
1724 assert_select 'select[name=?]', 'issue[project_id]', 0
1724 assert_select 'select[name=?]', 'issue[project_id]', 0
1725 assert_select 'select[name=?]', 'issue[tracker_id]'
1725 assert_select 'select[name=?]', 'issue[tracker_id]'
1726 assert_select 'input[name=?]', 'issue[subject]'
1726 assert_select 'input[name=?]', 'issue[subject]'
1727 assert_select 'textarea[name=?]', 'issue[description]'
1727 assert_select 'textarea[name=?]', 'issue[description]'
1728 assert_select 'select[name=?]', 'issue[status_id]'
1728 assert_select 'select[name=?]', 'issue[status_id]'
1729 assert_select 'select[name=?]', 'issue[priority_id]'
1729 assert_select 'select[name=?]', 'issue[priority_id]'
1730 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1730 assert_select 'select[name=?]', 'issue[assigned_to_id]'
1731 assert_select 'select[name=?]', 'issue[category_id]'
1731 assert_select 'select[name=?]', 'issue[category_id]'
1732 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1732 assert_select 'select[name=?]', 'issue[fixed_version_id]'
1733 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
1733 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
1734 assert_select 'input[name=?]', 'issue[start_date]'
1734 assert_select 'input[name=?]', 'issue[start_date]'
1735 assert_select 'input[name=?]', 'issue[due_date]'
1735 assert_select 'input[name=?]', 'issue[due_date]'
1736 assert_select 'select[name=?]', 'issue[done_ratio]'
1736 assert_select 'select[name=?]', 'issue[done_ratio]'
1737 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
1737 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
1738 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1738 assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
1739 end
1739 end
1740 end
1740 end
1741
1741
1742 def test_new_without_project_id
1742 def test_new_without_project_id
1743 @request.session[:user_id] = 2
1743 @request.session[:user_id] = 2
1744 get :new
1744 get :new
1745 assert_response :success
1745 assert_response :success
1746
1746
1747 assert_select 'form#issue-form[action=?]', '/issues'
1747 assert_select 'form#issue-form[action=?]', '/issues'
1748 assert_select 'form#issue-form' do
1748 assert_select 'form#issue-form' do
1749 assert_select 'select[name=?]', 'issue[project_id]'
1749 assert_select 'select[name=?]', 'issue[project_id]'
1750 end
1750 end
1751 end
1751 end
1752
1752
1753 def test_new_should_select_default_status
1753 def test_new_should_select_default_status
1754 @request.session[:user_id] = 2
1754 @request.session[:user_id] = 2
1755
1755
1756 get :new, :project_id => 1
1756 get :new, :project_id => 1
1757 assert_response :success
1757 assert_response :success
1758 assert_select 'select[name=?]', 'issue[status_id]' do
1758 assert_select 'select[name=?]', 'issue[status_id]' do
1759 assert_select 'option[value="1"][selected=selected]'
1759 assert_select 'option[value="1"][selected=selected]'
1760 end
1760 end
1761 assert_select 'input[name=was_default_status][value="1"]'
1761 assert_select 'input[name=was_default_status][value="1"]'
1762 end
1762 end
1763
1763
1764 def test_new_should_propose_allowed_statuses
1764 def test_new_should_propose_allowed_statuses
1765 WorkflowTransition.delete_all
1765 WorkflowTransition.delete_all
1766 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 1)
1766 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 1)
1767 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 3)
1767 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 3)
1768 @request.session[:user_id] = 2
1768 @request.session[:user_id] = 2
1769
1769
1770 get :new, :project_id => 1
1770 get :new, :project_id => 1
1771 assert_response :success
1771 assert_response :success
1772 assert_select 'select[name=?]', 'issue[status_id]' do
1772 assert_select 'select[name=?]', 'issue[status_id]' do
1773 assert_select 'option[value="1"]'
1773 assert_select 'option[value="1"]'
1774 assert_select 'option[value="3"]'
1774 assert_select 'option[value="3"]'
1775 assert_select 'option', 2
1775 assert_select 'option', 2
1776 assert_select 'option[value="1"][selected=selected]'
1776 assert_select 'option[value="1"][selected=selected]'
1777 end
1777 end
1778 end
1778 end
1779
1779
1780 def test_new_should_propose_allowed_statuses_without_default_status_allowed
1780 def test_new_should_propose_allowed_statuses_without_default_status_allowed
1781 WorkflowTransition.delete_all
1781 WorkflowTransition.delete_all
1782 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 2)
1782 WorkflowTransition.create!(:tracker_id => 1, :role_id => 1, :old_status_id => 0, :new_status_id => 2)
1783 assert_equal 1, Tracker.find(1).default_status_id
1783 assert_equal 1, Tracker.find(1).default_status_id
1784 @request.session[:user_id] = 2
1784 @request.session[:user_id] = 2
1785
1785
1786 get :new, :project_id => 1
1786 get :new, :project_id => 1
1787 assert_response :success
1787 assert_response :success
1788 assert_select 'select[name=?]', 'issue[status_id]' do
1788 assert_select 'select[name=?]', 'issue[status_id]' do
1789 assert_select 'option[value="2"]'
1789 assert_select 'option[value="2"]'
1790 assert_select 'option', 1
1790 assert_select 'option', 1
1791 assert_select 'option[value="2"][selected=selected]'
1791 assert_select 'option[value="2"][selected=selected]'
1792 end
1792 end
1793 end
1793 end
1794
1794
1795 def test_new_should_propose_allowed_trackers
1795 def test_new_should_propose_allowed_trackers
1796 role = Role.find(1)
1796 role = Role.find(1)
1797 role.set_permission_trackers 'add_issues', [1, 3]
1797 role.set_permission_trackers 'add_issues', [1, 3]
1798 role.save!
1798 role.save!
1799 @request.session[:user_id] = 2
1799 @request.session[:user_id] = 2
1800
1800
1801 get :new, :project_id => 1
1801 get :new, :project_id => 1
1802 assert_response :success
1802 assert_response :success
1803 assert_select 'select[name=?]', 'issue[tracker_id]' do
1803 assert_select 'select[name=?]', 'issue[tracker_id]' do
1804 assert_select 'option', 2
1804 assert_select 'option', 2
1805 assert_select 'option[value="1"]'
1805 assert_select 'option[value="1"]'
1806 assert_select 'option[value="3"]'
1806 assert_select 'option[value="3"]'
1807 end
1807 end
1808 end
1808 end
1809
1809
1810 def test_new_without_allowed_trackers_should_respond_with_403
1810 def test_new_without_allowed_trackers_should_respond_with_403
1811 role = Role.find(1)
1811 role = Role.find(1)
1812 role.set_permission_trackers 'add_issues', []
1812 role.set_permission_trackers 'add_issues', []
1813 role.save!
1813 role.save!
1814 @request.session[:user_id] = 2
1814 @request.session[:user_id] = 2
1815
1815
1816 get :new, :project_id => 1
1816 get :new, :project_id => 1
1817 assert_response 403
1817 assert_response 403
1818 end
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 def test_new_should_preselect_default_version
1838 def test_new_should_preselect_default_version
1821 version = Version.generate!(:project_id => 1)
1839 version = Version.generate!(:project_id => 1)
1822 Project.find(1).update_attribute :default_version_id, version.id
1840 Project.find(1).update_attribute :default_version_id, version.id
1823 @request.session[:user_id] = 2
1841 @request.session[:user_id] = 2
1824
1842
1825 get :new, :project_id => 1
1843 get :new, :project_id => 1
1826 assert_response :success
1844 assert_response :success
1827 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
1845 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
1828 assert_select 'option[value=?][selected=selected]', version.id.to_s
1846 assert_select 'option[value=?][selected=selected]', version.id.to_s
1829 end
1847 end
1830 end
1848 end
1831
1849
1832 def test_get_new_with_list_custom_field
1850 def test_get_new_with_list_custom_field
1833 @request.session[:user_id] = 2
1851 @request.session[:user_id] = 2
1834 get :new, :project_id => 1, :tracker_id => 1
1852 get :new, :project_id => 1, :tracker_id => 1
1835 assert_response :success
1853 assert_response :success
1836
1854
1837 assert_select 'select.list_cf[name=?]', 'issue[custom_field_values][1]' do
1855 assert_select 'select.list_cf[name=?]', 'issue[custom_field_values][1]' do
1838 assert_select 'option', 4
1856 assert_select 'option', 4
1839 assert_select 'option[value=MySQL]', :text => 'MySQL'
1857 assert_select 'option[value=MySQL]', :text => 'MySQL'
1840 end
1858 end
1841 end
1859 end
1842
1860
1843 def test_get_new_with_multi_custom_field
1861 def test_get_new_with_multi_custom_field
1844 field = IssueCustomField.find(1)
1862 field = IssueCustomField.find(1)
1845 field.update_attribute :multiple, true
1863 field.update_attribute :multiple, true
1846
1864
1847 @request.session[:user_id] = 2
1865 @request.session[:user_id] = 2
1848 get :new, :project_id => 1, :tracker_id => 1
1866 get :new, :project_id => 1, :tracker_id => 1
1849 assert_response :success
1867 assert_response :success
1850
1868
1851 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
1869 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
1852 assert_select 'option', 3
1870 assert_select 'option', 3
1853 assert_select 'option[value=MySQL]', :text => 'MySQL'
1871 assert_select 'option[value=MySQL]', :text => 'MySQL'
1854 end
1872 end
1855 assert_select 'input[name=?][type=hidden][value=?]', 'issue[custom_field_values][1][]', ''
1873 assert_select 'input[name=?][type=hidden][value=?]', 'issue[custom_field_values][1][]', ''
1856 end
1874 end
1857
1875
1858 def test_get_new_with_multi_user_custom_field
1876 def test_get_new_with_multi_user_custom_field
1859 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1877 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1860 :tracker_ids => [1], :is_for_all => true)
1878 :tracker_ids => [1], :is_for_all => true)
1861
1879
1862 @request.session[:user_id] = 2
1880 @request.session[:user_id] = 2
1863 get :new, :project_id => 1, :tracker_id => 1
1881 get :new, :project_id => 1, :tracker_id => 1
1864 assert_response :success
1882 assert_response :success
1865
1883
1866 assert_select 'select[name=?][multiple=multiple]', "issue[custom_field_values][#{field.id}][]" do
1884 assert_select 'select[name=?][multiple=multiple]', "issue[custom_field_values][#{field.id}][]" do
1867 assert_select 'option', Project.find(1).users.count
1885 assert_select 'option', Project.find(1).users.count
1868 assert_select 'option[value="2"]', :text => 'John Smith'
1886 assert_select 'option[value="2"]', :text => 'John Smith'
1869 end
1887 end
1870 assert_select 'input[name=?][type=hidden][value=?]', "issue[custom_field_values][#{field.id}][]", ''
1888 assert_select 'input[name=?][type=hidden][value=?]', "issue[custom_field_values][#{field.id}][]", ''
1871 end
1889 end
1872
1890
1873 def test_get_new_with_date_custom_field
1891 def test_get_new_with_date_custom_field
1874 field = IssueCustomField.create!(:name => 'Date', :field_format => 'date', :tracker_ids => [1], :is_for_all => true)
1892 field = IssueCustomField.create!(:name => 'Date', :field_format => 'date', :tracker_ids => [1], :is_for_all => true)
1875
1893
1876 @request.session[:user_id] = 2
1894 @request.session[:user_id] = 2
1877 get :new, :project_id => 1, :tracker_id => 1
1895 get :new, :project_id => 1, :tracker_id => 1
1878 assert_response :success
1896 assert_response :success
1879
1897
1880 assert_select 'input[name=?]', "issue[custom_field_values][#{field.id}]"
1898 assert_select 'input[name=?]', "issue[custom_field_values][#{field.id}]"
1881 end
1899 end
1882
1900
1883 def test_get_new_with_text_custom_field
1901 def test_get_new_with_text_custom_field
1884 field = IssueCustomField.create!(:name => 'Text', :field_format => 'text', :tracker_ids => [1], :is_for_all => true)
1902 field = IssueCustomField.create!(:name => 'Text', :field_format => 'text', :tracker_ids => [1], :is_for_all => true)
1885
1903
1886 @request.session[:user_id] = 2
1904 @request.session[:user_id] = 2
1887 get :new, :project_id => 1, :tracker_id => 1
1905 get :new, :project_id => 1, :tracker_id => 1
1888 assert_response :success
1906 assert_response :success
1889
1907
1890 assert_select 'textarea[name=?]', "issue[custom_field_values][#{field.id}]"
1908 assert_select 'textarea[name=?]', "issue[custom_field_values][#{field.id}]"
1891 end
1909 end
1892
1910
1893 def test_get_new_without_default_start_date_is_creation_date
1911 def test_get_new_without_default_start_date_is_creation_date
1894 with_settings :default_issue_start_date_to_creation_date => 0 do
1912 with_settings :default_issue_start_date_to_creation_date => 0 do
1895 @request.session[:user_id] = 2
1913 @request.session[:user_id] = 2
1896 get :new, :project_id => 1, :tracker_id => 1
1914 get :new, :project_id => 1, :tracker_id => 1
1897 assert_response :success
1915 assert_response :success
1898 assert_select 'input[name=?]', 'issue[start_date]'
1916 assert_select 'input[name=?]', 'issue[start_date]'
1899 assert_select 'input[name=?][value]', 'issue[start_date]', 0
1917 assert_select 'input[name=?][value]', 'issue[start_date]', 0
1900 end
1918 end
1901 end
1919 end
1902
1920
1903 def test_get_new_with_default_start_date_is_creation_date
1921 def test_get_new_with_default_start_date_is_creation_date
1904 with_settings :default_issue_start_date_to_creation_date => 1 do
1922 with_settings :default_issue_start_date_to_creation_date => 1 do
1905 @request.session[:user_id] = 2
1923 @request.session[:user_id] = 2
1906 get :new, :project_id => 1, :tracker_id => 1
1924 get :new, :project_id => 1, :tracker_id => 1
1907 assert_response :success
1925 assert_response :success
1908 assert_select 'input[name=?][value=?]', 'issue[start_date]',
1926 assert_select 'input[name=?][value=?]', 'issue[start_date]',
1909 Date.today.to_s
1927 Date.today.to_s
1910 end
1928 end
1911 end
1929 end
1912
1930
1913 def test_get_new_form_should_allow_attachment_upload
1931 def test_get_new_form_should_allow_attachment_upload
1914 @request.session[:user_id] = 2
1932 @request.session[:user_id] = 2
1915 get :new, :project_id => 1, :tracker_id => 1
1933 get :new, :project_id => 1, :tracker_id => 1
1916 assert_response :success
1934 assert_response :success
1917
1935
1918 assert_select 'form[id=issue-form][method=post][enctype="multipart/form-data"]' do
1936 assert_select 'form[id=issue-form][method=post][enctype="multipart/form-data"]' do
1919 assert_select 'input[name=?][type=file]', 'attachments[dummy][file]'
1937 assert_select 'input[name=?][type=file]', 'attachments[dummy][file]'
1920 end
1938 end
1921 end
1939 end
1922
1940
1923 def test_get_new_should_prefill_the_form_from_params
1941 def test_get_new_should_prefill_the_form_from_params
1924 @request.session[:user_id] = 2
1942 @request.session[:user_id] = 2
1925 get :new, :project_id => 1,
1943 get :new, :project_id => 1,
1926 :issue => {:tracker_id => 3, :description => 'Prefilled', :custom_field_values => {'2' => 'Custom field value'}}
1944 :issue => {:tracker_id => 3, :description => 'Prefilled', :custom_field_values => {'2' => 'Custom field value'}}
1927
1945
1928 assert_select 'select[name=?]', 'issue[tracker_id]' do
1946 assert_select 'select[name=?]', 'issue[tracker_id]' do
1929 assert_select 'option[value="3"][selected=selected]'
1947 assert_select 'option[value="3"][selected=selected]'
1930 end
1948 end
1931 assert_select 'textarea[name=?]', 'issue[description]', :text => /Prefilled/
1949 assert_select 'textarea[name=?]', 'issue[description]', :text => /Prefilled/
1932 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Custom field value'
1950 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Custom field value'
1933 end
1951 end
1934
1952
1935 def test_get_new_should_mark_required_fields
1953 def test_get_new_should_mark_required_fields
1936 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1954 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1937 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1955 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1938 WorkflowPermission.delete_all
1956 WorkflowPermission.delete_all
1939 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
1957 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
1940 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
1958 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
1941 @request.session[:user_id] = 2
1959 @request.session[:user_id] = 2
1942
1960
1943 get :new, :project_id => 1
1961 get :new, :project_id => 1
1944 assert_response :success
1962 assert_response :success
1945
1963
1946 assert_select 'label[for=issue_start_date]' do
1964 assert_select 'label[for=issue_start_date]' do
1947 assert_select 'span[class=required]', 0
1965 assert_select 'span[class=required]', 0
1948 end
1966 end
1949 assert_select 'label[for=issue_due_date]' do
1967 assert_select 'label[for=issue_due_date]' do
1950 assert_select 'span[class=required]'
1968 assert_select 'span[class=required]'
1951 end
1969 end
1952 assert_select 'label[for=?]', "issue_custom_field_values_#{cf1.id}" do
1970 assert_select 'label[for=?]', "issue_custom_field_values_#{cf1.id}" do
1953 assert_select 'span[class=required]', 0
1971 assert_select 'span[class=required]', 0
1954 end
1972 end
1955 assert_select 'label[for=?]', "issue_custom_field_values_#{cf2.id}" do
1973 assert_select 'label[for=?]', "issue_custom_field_values_#{cf2.id}" do
1956 assert_select 'span[class=required]'
1974 assert_select 'span[class=required]'
1957 end
1975 end
1958 end
1976 end
1959
1977
1960 def test_get_new_should_not_display_readonly_fields
1978 def test_get_new_should_not_display_readonly_fields
1961 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1979 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1962 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1980 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1963 WorkflowPermission.delete_all
1981 WorkflowPermission.delete_all
1964 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
1982 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
1965 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
1983 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
1966 @request.session[:user_id] = 2
1984 @request.session[:user_id] = 2
1967
1985
1968 get :new, :project_id => 1
1986 get :new, :project_id => 1
1969 assert_response :success
1987 assert_response :success
1970
1988
1971 assert_select 'input[name=?]', 'issue[start_date]'
1989 assert_select 'input[name=?]', 'issue[start_date]'
1972 assert_select 'input[name=?]', 'issue[due_date]', 0
1990 assert_select 'input[name=?]', 'issue[due_date]', 0
1973 assert_select 'input[name=?]', "issue[custom_field_values][#{cf1.id}]"
1991 assert_select 'input[name=?]', "issue[custom_field_values][#{cf1.id}]"
1974 assert_select 'input[name=?]', "issue[custom_field_values][#{cf2.id}]", 0
1992 assert_select 'input[name=?]', "issue[custom_field_values][#{cf2.id}]", 0
1975 end
1993 end
1976
1994
1977 def test_new_with_tracker_set_as_readonly_should_accept_status
1995 def test_new_with_tracker_set_as_readonly_should_accept_status
1978 WorkflowPermission.delete_all
1996 WorkflowPermission.delete_all
1979 [1, 2].each do |status_id|
1997 [1, 2].each do |status_id|
1980 WorkflowPermission.create!(:tracker_id => 1, :old_status_id => status_id, :role_id => 1, :field_name => 'tracker_id', :rule => 'readonly')
1998 WorkflowPermission.create!(:tracker_id => 1, :old_status_id => status_id, :role_id => 1, :field_name => 'tracker_id', :rule => 'readonly')
1981 end
1999 end
1982 @request.session[:user_id] = 2
2000 @request.session[:user_id] = 2
1983
2001
1984 get :new, :project_id => 1, :issue => {:status_id => 2}
2002 get :new, :project_id => 1, :issue => {:status_id => 2}
1985 assert_select 'select[name=?]', 'issue[tracker_id]', 0
2003 assert_select 'select[name=?]', 'issue[tracker_id]', 0
1986 assert_select 'select[name=?]', 'issue[status_id]' do
2004 assert_select 'select[name=?]', 'issue[status_id]' do
1987 assert_select 'option[value=?][selected=selected]', '2'
2005 assert_select 'option[value=?][selected=selected]', '2'
1988 end
2006 end
1989 end
2007 end
1990
2008
1991 def test_get_new_without_tracker_id
2009 def test_get_new_without_tracker_id
1992 @request.session[:user_id] = 2
2010 @request.session[:user_id] = 2
1993 get :new, :project_id => 1
2011 get :new, :project_id => 1
1994 assert_response :success
2012 assert_response :success
1995
2013
1996 assert_select 'select[name=?]', 'issue[tracker_id]' do
2014 assert_select 'select[name=?]', 'issue[tracker_id]' do
1997 assert_select 'option[value=?][selected=selected]', Project.find(1).trackers.first.id.to_s
2015 assert_select 'option[value=?][selected=selected]', Project.find(1).trackers.first.id.to_s
1998 end
2016 end
1999 end
2017 end
2000
2018
2001 def test_get_new_with_no_default_status_should_display_an_error
2019 def test_get_new_with_no_default_status_should_display_an_error
2002 @request.session[:user_id] = 2
2020 @request.session[:user_id] = 2
2003 IssueStatus.delete_all
2021 IssueStatus.delete_all
2004
2022
2005 get :new, :project_id => 1
2023 get :new, :project_id => 1
2006 assert_response 500
2024 assert_response 500
2007 assert_select_error /No default issue/
2025 assert_select_error /No default issue/
2008 end
2026 end
2009
2027
2010 def test_get_new_with_no_tracker_should_display_an_error
2028 def test_get_new_with_no_tracker_should_display_an_error
2011 @request.session[:user_id] = 2
2029 @request.session[:user_id] = 2
2012 Tracker.delete_all
2030 Tracker.delete_all
2013
2031
2014 get :new, :project_id => 1
2032 get :new, :project_id => 1
2015 assert_response 500
2033 assert_response 500
2016 assert_select_error /No tracker/
2034 assert_select_error /No tracker/
2017 end
2035 end
2018
2036
2019 def test_new_with_invalid_project_id
2037 def test_new_with_invalid_project_id
2020 @request.session[:user_id] = 1
2038 @request.session[:user_id] = 1
2021 get :new, :project_id => 'invalid'
2039 get :new, :project_id => 'invalid'
2022 assert_response 404
2040 assert_response 404
2023 end
2041 end
2024
2042
2025 def test_new_with_parent_id_should_only_propose_valid_trackers
2043 def test_new_with_parent_id_should_only_propose_valid_trackers
2026 @request.session[:user_id] = 2
2044 @request.session[:user_id] = 2
2027 t = Tracker.find(3)
2045 t = Tracker.find(3)
2028 assert !t.disabled_core_fields.include?('parent_issue_id')
2046 assert !t.disabled_core_fields.include?('parent_issue_id')
2029
2047
2030 get :new, :project_id => 1, issue: { parent_issue_id: 1 }
2048 get :new, :project_id => 1, issue: { parent_issue_id: 1 }
2031 assert_response :success
2049 assert_response :success
2032 assert_select 'option', text: /#{t.name}/, count: 1
2050 assert_select 'option', text: /#{t.name}/, count: 1
2033
2051
2034 t.core_fields = Tracker::CORE_FIELDS - ['parent_issue_id']
2052 t.core_fields = Tracker::CORE_FIELDS - ['parent_issue_id']
2035 t.save!
2053 t.save!
2036 assert t.disabled_core_fields.include?('parent_issue_id')
2054 assert t.disabled_core_fields.include?('parent_issue_id')
2037 get :new, :project_id => 1, issue: { parent_issue_id: 1 }
2055 get :new, :project_id => 1, issue: { parent_issue_id: 1 }
2038 assert_response :success
2056 assert_response :success
2039 assert_select 'option', text: /#{t.name}/, count: 0
2057 assert_select 'option', text: /#{t.name}/, count: 0
2040 end
2058 end
2041
2059
2042 def test_update_form_for_new_issue
2060 def test_update_form_for_new_issue
2043 @request.session[:user_id] = 2
2061 @request.session[:user_id] = 2
2044 xhr :post, :new, :project_id => 1,
2062 xhr :post, :new, :project_id => 1,
2045 :issue => {:tracker_id => 2,
2063 :issue => {:tracker_id => 2,
2046 :subject => 'This is the test_new issue',
2064 :subject => 'This is the test_new issue',
2047 :description => 'This is the description',
2065 :description => 'This is the description',
2048 :priority_id => 5}
2066 :priority_id => 5}
2049 assert_response :success
2067 assert_response :success
2050 assert_equal 'text/javascript', response.content_type
2068 assert_equal 'text/javascript', response.content_type
2051 assert_include 'This is the test_new issue', response.body
2069 assert_include 'This is the test_new issue', response.body
2052 end
2070 end
2053
2071
2054 def test_update_form_for_new_issue_should_propose_transitions_based_on_initial_status
2072 def test_update_form_for_new_issue_should_propose_transitions_based_on_initial_status
2055 @request.session[:user_id] = 2
2073 @request.session[:user_id] = 2
2056 WorkflowTransition.delete_all
2074 WorkflowTransition.delete_all
2057 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 2)
2075 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 2)
2058 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 5)
2076 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 0, :new_status_id => 5)
2059 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4)
2077 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4)
2060
2078
2061 post :new, :project_id => 1,
2079 post :new, :project_id => 1,
2062 :issue => {:tracker_id => 1,
2080 :issue => {:tracker_id => 1,
2063 :status_id => 5,
2081 :status_id => 5,
2064 :subject => 'This is an issue'}
2082 :subject => 'This is an issue'}
2065
2083
2066 assert_select 'select[name=?]', 'issue[status_id]' do
2084 assert_select 'select[name=?]', 'issue[status_id]' do
2067 assert_select 'option[value=?][selected=selected]', '5'
2085 assert_select 'option[value=?][selected=selected]', '5'
2068 assert_select 'option[value=?]', '2'
2086 assert_select 'option[value=?]', '2'
2069 assert_select 'option', :count => 2
2087 assert_select 'option', :count => 2
2070 end
2088 end
2071 end
2089 end
2072
2090
2073 def test_update_form_with_default_status_should_ignore_submitted_status_id_if_equals
2091 def test_update_form_with_default_status_should_ignore_submitted_status_id_if_equals
2074 @request.session[:user_id] = 2
2092 @request.session[:user_id] = 2
2075 tracker = Tracker.find(2)
2093 tracker = Tracker.find(2)
2076 tracker.update! :default_status_id => 2
2094 tracker.update! :default_status_id => 2
2077 tracker.generate_transitions! 2, 1, :clear => true
2095 tracker.generate_transitions! 2, 1, :clear => true
2078
2096
2079 post :new, :project_id => 1,
2097 post :new, :project_id => 1,
2080 :issue => {:tracker_id => 2,
2098 :issue => {:tracker_id => 2,
2081 :status_id => 1},
2099 :status_id => 1},
2082 :was_default_status => 1
2100 :was_default_status => 1
2083 assert_response :success
2101 assert_response :success
2084
2102
2085 assert_select 'select[name=?]', 'issue[status_id]' do
2103 assert_select 'select[name=?]', 'issue[status_id]' do
2086 assert_select 'option[value=?][selected=selected]', '2'
2104 assert_select 'option[value=?][selected=selected]', '2'
2087 end
2105 end
2088 end
2106 end
2089
2107
2090 def test_update_form_for_new_issue_should_ignore_version_when_changing_project
2108 def test_update_form_for_new_issue_should_ignore_version_when_changing_project
2091 version = Version.generate!(:project_id => 1)
2109 version = Version.generate!(:project_id => 1)
2092 Project.find(1).update_attribute :default_version_id, version.id
2110 Project.find(1).update_attribute :default_version_id, version.id
2093 @request.session[:user_id] = 2
2111 @request.session[:user_id] = 2
2094
2112
2095 post :new, :issue => {:project_id => 1,
2113 post :new, :issue => {:project_id => 1,
2096 :fixed_version_id => ''},
2114 :fixed_version_id => ''},
2097 :form_update_triggered_by => 'issue_project_id'
2115 :form_update_triggered_by => 'issue_project_id'
2098 assert_response :success
2116 assert_response :success
2099
2117
2100 assert_select 'select[name=?]', 'issue[project_id]' do
2118 assert_select 'select[name=?]', 'issue[project_id]' do
2101 assert_select 'option[value=?][selected=selected]', '1'
2119 assert_select 'option[value=?][selected=selected]', '1'
2102 end
2120 end
2103 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
2121 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
2104 assert_select 'option[value=?][selected=selected]', version.id.to_s
2122 assert_select 'option[value=?][selected=selected]', version.id.to_s
2105 end
2123 end
2106 end
2124 end
2107
2125
2108 def test_post_create
2126 def test_post_create
2109 @request.session[:user_id] = 2
2127 @request.session[:user_id] = 2
2110 assert_difference 'Issue.count' do
2128 assert_difference 'Issue.count' do
2111 assert_no_difference 'Journal.count' do
2129 assert_no_difference 'Journal.count' do
2112 post :create, :project_id => 1,
2130 post :create, :project_id => 1,
2113 :issue => {:tracker_id => 3,
2131 :issue => {:tracker_id => 3,
2114 :status_id => 2,
2132 :status_id => 2,
2115 :subject => 'This is the test_new issue',
2133 :subject => 'This is the test_new issue',
2116 :description => 'This is the description',
2134 :description => 'This is the description',
2117 :priority_id => 5,
2135 :priority_id => 5,
2118 :start_date => '2010-11-07',
2136 :start_date => '2010-11-07',
2119 :estimated_hours => '',
2137 :estimated_hours => '',
2120 :custom_field_values => {'2' => 'Value for field 2'}}
2138 :custom_field_values => {'2' => 'Value for field 2'}}
2121 end
2139 end
2122 end
2140 end
2123 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2141 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2124
2142
2125 issue = Issue.find_by_subject('This is the test_new issue')
2143 issue = Issue.find_by_subject('This is the test_new issue')
2126 assert_not_nil issue
2144 assert_not_nil issue
2127 assert_equal 2, issue.author_id
2145 assert_equal 2, issue.author_id
2128 assert_equal 3, issue.tracker_id
2146 assert_equal 3, issue.tracker_id
2129 assert_equal 2, issue.status_id
2147 assert_equal 2, issue.status_id
2130 assert_equal Date.parse('2010-11-07'), issue.start_date
2148 assert_equal Date.parse('2010-11-07'), issue.start_date
2131 assert_nil issue.estimated_hours
2149 assert_nil issue.estimated_hours
2132 v = issue.custom_values.where(:custom_field_id => 2).first
2150 v = issue.custom_values.where(:custom_field_id => 2).first
2133 assert_not_nil v
2151 assert_not_nil v
2134 assert_equal 'Value for field 2', v.value
2152 assert_equal 'Value for field 2', v.value
2135 end
2153 end
2136
2154
2137 def test_post_new_with_group_assignment
2155 def test_post_new_with_group_assignment
2138 group = Group.find(11)
2156 group = Group.find(11)
2139 project = Project.find(1)
2157 project = Project.find(1)
2140 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
2158 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
2141
2159
2142 with_settings :issue_group_assignment => '1' do
2160 with_settings :issue_group_assignment => '1' do
2143 @request.session[:user_id] = 2
2161 @request.session[:user_id] = 2
2144 assert_difference 'Issue.count' do
2162 assert_difference 'Issue.count' do
2145 post :create, :project_id => project.id,
2163 post :create, :project_id => project.id,
2146 :issue => {:tracker_id => 3,
2164 :issue => {:tracker_id => 3,
2147 :status_id => 1,
2165 :status_id => 1,
2148 :subject => 'This is the test_new_with_group_assignment issue',
2166 :subject => 'This is the test_new_with_group_assignment issue',
2149 :assigned_to_id => group.id}
2167 :assigned_to_id => group.id}
2150 end
2168 end
2151 end
2169 end
2152 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2170 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2153
2171
2154 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
2172 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
2155 assert_not_nil issue
2173 assert_not_nil issue
2156 assert_equal group, issue.assigned_to
2174 assert_equal group, issue.assigned_to
2157 end
2175 end
2158
2176
2159 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
2177 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
2160 with_settings :default_issue_start_date_to_creation_date => 0 do
2178 with_settings :default_issue_start_date_to_creation_date => 0 do
2161 @request.session[:user_id] = 2
2179 @request.session[:user_id] = 2
2162 assert_difference 'Issue.count' do
2180 assert_difference 'Issue.count' do
2163 post :create, :project_id => 1,
2181 post :create, :project_id => 1,
2164 :issue => {:tracker_id => 3,
2182 :issue => {:tracker_id => 3,
2165 :status_id => 2,
2183 :status_id => 2,
2166 :subject => 'This is the test_new issue',
2184 :subject => 'This is the test_new issue',
2167 :description => 'This is the description',
2185 :description => 'This is the description',
2168 :priority_id => 5,
2186 :priority_id => 5,
2169 :estimated_hours => '',
2187 :estimated_hours => '',
2170 :custom_field_values => {'2' => 'Value for field 2'}}
2188 :custom_field_values => {'2' => 'Value for field 2'}}
2171 end
2189 end
2172 assert_redirected_to :controller => 'issues', :action => 'show',
2190 assert_redirected_to :controller => 'issues', :action => 'show',
2173 :id => Issue.last.id
2191 :id => Issue.last.id
2174 issue = Issue.find_by_subject('This is the test_new issue')
2192 issue = Issue.find_by_subject('This is the test_new issue')
2175 assert_not_nil issue
2193 assert_not_nil issue
2176 assert_nil issue.start_date
2194 assert_nil issue.start_date
2177 end
2195 end
2178 end
2196 end
2179
2197
2180 def test_post_create_without_start_date_and_default_start_date_is_creation_date
2198 def test_post_create_without_start_date_and_default_start_date_is_creation_date
2181 with_settings :default_issue_start_date_to_creation_date => 1 do
2199 with_settings :default_issue_start_date_to_creation_date => 1 do
2182 @request.session[:user_id] = 2
2200 @request.session[:user_id] = 2
2183 assert_difference 'Issue.count' do
2201 assert_difference 'Issue.count' do
2184 post :create, :project_id => 1,
2202 post :create, :project_id => 1,
2185 :issue => {:tracker_id => 3,
2203 :issue => {:tracker_id => 3,
2186 :status_id => 2,
2204 :status_id => 2,
2187 :subject => 'This is the test_new issue',
2205 :subject => 'This is the test_new issue',
2188 :description => 'This is the description',
2206 :description => 'This is the description',
2189 :priority_id => 5,
2207 :priority_id => 5,
2190 :estimated_hours => '',
2208 :estimated_hours => '',
2191 :custom_field_values => {'2' => 'Value for field 2'}}
2209 :custom_field_values => {'2' => 'Value for field 2'}}
2192 end
2210 end
2193 assert_redirected_to :controller => 'issues', :action => 'show',
2211 assert_redirected_to :controller => 'issues', :action => 'show',
2194 :id => Issue.last.id
2212 :id => Issue.last.id
2195 issue = Issue.find_by_subject('This is the test_new issue')
2213 issue = Issue.find_by_subject('This is the test_new issue')
2196 assert_not_nil issue
2214 assert_not_nil issue
2197 assert_equal Date.today, issue.start_date
2215 assert_equal Date.today, issue.start_date
2198 end
2216 end
2199 end
2217 end
2200
2218
2201 def test_post_create_and_continue
2219 def test_post_create_and_continue
2202 @request.session[:user_id] = 2
2220 @request.session[:user_id] = 2
2203 assert_difference 'Issue.count' do
2221 assert_difference 'Issue.count' do
2204 post :create, :project_id => 1,
2222 post :create, :project_id => 1,
2205 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
2223 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
2206 :continue => ''
2224 :continue => ''
2207 end
2225 end
2208
2226
2209 issue = Issue.order('id DESC').first
2227 issue = Issue.order('id DESC').first
2210 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
2228 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
2211 assert_not_nil flash[:notice], "flash was not set"
2229 assert_not_nil flash[:notice], "flash was not set"
2212 assert_select_in flash[:notice],
2230 assert_select_in flash[:notice],
2213 'a[href=?][title=?]', "/issues/#{issue.id}", "This is first issue", :text => "##{issue.id}"
2231 'a[href=?][title=?]', "/issues/#{issue.id}", "This is first issue", :text => "##{issue.id}"
2214 end
2232 end
2215
2233
2216 def test_post_create_without_custom_fields_param
2234 def test_post_create_without_custom_fields_param
2217 @request.session[:user_id] = 2
2235 @request.session[:user_id] = 2
2218 assert_difference 'Issue.count' do
2236 assert_difference 'Issue.count' do
2219 post :create, :project_id => 1,
2237 post :create, :project_id => 1,
2220 :issue => {:tracker_id => 1,
2238 :issue => {:tracker_id => 1,
2221 :subject => 'This is the test_new issue',
2239 :subject => 'This is the test_new issue',
2222 :description => 'This is the description',
2240 :description => 'This is the description',
2223 :priority_id => 5}
2241 :priority_id => 5}
2224 end
2242 end
2225 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2243 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2226 end
2244 end
2227
2245
2228 def test_post_create_with_multi_custom_field
2246 def test_post_create_with_multi_custom_field
2229 field = IssueCustomField.find_by_name('Database')
2247 field = IssueCustomField.find_by_name('Database')
2230 field.update_attribute(:multiple, true)
2248 field.update_attribute(:multiple, true)
2231
2249
2232 @request.session[:user_id] = 2
2250 @request.session[:user_id] = 2
2233 assert_difference 'Issue.count' do
2251 assert_difference 'Issue.count' do
2234 post :create, :project_id => 1,
2252 post :create, :project_id => 1,
2235 :issue => {:tracker_id => 1,
2253 :issue => {:tracker_id => 1,
2236 :subject => 'This is the test_new issue',
2254 :subject => 'This is the test_new issue',
2237 :description => 'This is the description',
2255 :description => 'This is the description',
2238 :priority_id => 5,
2256 :priority_id => 5,
2239 :custom_field_values => {'1' => ['', 'MySQL', 'Oracle']}}
2257 :custom_field_values => {'1' => ['', 'MySQL', 'Oracle']}}
2240 end
2258 end
2241 assert_response 302
2259 assert_response 302
2242 issue = Issue.order('id DESC').first
2260 issue = Issue.order('id DESC').first
2243 assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
2261 assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
2244 end
2262 end
2245
2263
2246 def test_post_create_with_empty_multi_custom_field
2264 def test_post_create_with_empty_multi_custom_field
2247 field = IssueCustomField.find_by_name('Database')
2265 field = IssueCustomField.find_by_name('Database')
2248 field.update_attribute(:multiple, true)
2266 field.update_attribute(:multiple, true)
2249
2267
2250 @request.session[:user_id] = 2
2268 @request.session[:user_id] = 2
2251 assert_difference 'Issue.count' do
2269 assert_difference 'Issue.count' do
2252 post :create, :project_id => 1,
2270 post :create, :project_id => 1,
2253 :issue => {:tracker_id => 1,
2271 :issue => {:tracker_id => 1,
2254 :subject => 'This is the test_new issue',
2272 :subject => 'This is the test_new issue',
2255 :description => 'This is the description',
2273 :description => 'This is the description',
2256 :priority_id => 5,
2274 :priority_id => 5,
2257 :custom_field_values => {'1' => ['']}}
2275 :custom_field_values => {'1' => ['']}}
2258 end
2276 end
2259 assert_response 302
2277 assert_response 302
2260 issue = Issue.order('id DESC').first
2278 issue = Issue.order('id DESC').first
2261 assert_equal [''], issue.custom_field_value(1).sort
2279 assert_equal [''], issue.custom_field_value(1).sort
2262 end
2280 end
2263
2281
2264 def test_post_create_with_multi_user_custom_field
2282 def test_post_create_with_multi_user_custom_field
2265 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
2283 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
2266 :tracker_ids => [1], :is_for_all => true)
2284 :tracker_ids => [1], :is_for_all => true)
2267
2285
2268 @request.session[:user_id] = 2
2286 @request.session[:user_id] = 2
2269 assert_difference 'Issue.count' do
2287 assert_difference 'Issue.count' do
2270 post :create, :project_id => 1,
2288 post :create, :project_id => 1,
2271 :issue => {:tracker_id => 1,
2289 :issue => {:tracker_id => 1,
2272 :subject => 'This is the test_new issue',
2290 :subject => 'This is the test_new issue',
2273 :description => 'This is the description',
2291 :description => 'This is the description',
2274 :priority_id => 5,
2292 :priority_id => 5,
2275 :custom_field_values => {field.id.to_s => ['', '2', '3']}}
2293 :custom_field_values => {field.id.to_s => ['', '2', '3']}}
2276 end
2294 end
2277 assert_response 302
2295 assert_response 302
2278 issue = Issue.order('id DESC').first
2296 issue = Issue.order('id DESC').first
2279 assert_equal ['2', '3'], issue.custom_field_value(field).sort
2297 assert_equal ['2', '3'], issue.custom_field_value(field).sort
2280 end
2298 end
2281
2299
2282 def test_post_create_with_required_custom_field_and_without_custom_fields_param
2300 def test_post_create_with_required_custom_field_and_without_custom_fields_param
2283 field = IssueCustomField.find_by_name('Database')
2301 field = IssueCustomField.find_by_name('Database')
2284 field.update_attribute(:is_required, true)
2302 field.update_attribute(:is_required, true)
2285
2303
2286 @request.session[:user_id] = 2
2304 @request.session[:user_id] = 2
2287 assert_no_difference 'Issue.count' do
2305 assert_no_difference 'Issue.count' do
2288 post :create, :project_id => 1,
2306 post :create, :project_id => 1,
2289 :issue => {:tracker_id => 1,
2307 :issue => {:tracker_id => 1,
2290 :subject => 'This is the test_new issue',
2308 :subject => 'This is the test_new issue',
2291 :description => 'This is the description',
2309 :description => 'This is the description',
2292 :priority_id => 5}
2310 :priority_id => 5}
2293 end
2311 end
2294 assert_response :success
2312 assert_response :success
2295 assert_select_error /Database cannot be blank/
2313 assert_select_error /Database cannot be blank/
2296 end
2314 end
2297
2315
2298 def test_create_should_validate_required_fields
2316 def test_create_should_validate_required_fields
2299 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2317 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2300 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2318 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2301 WorkflowPermission.delete_all
2319 WorkflowPermission.delete_all
2302 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'required')
2320 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'required')
2303 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
2321 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
2304 @request.session[:user_id] = 2
2322 @request.session[:user_id] = 2
2305
2323
2306 assert_no_difference 'Issue.count' do
2324 assert_no_difference 'Issue.count' do
2307 post :create, :project_id => 1, :issue => {
2325 post :create, :project_id => 1, :issue => {
2308 :tracker_id => 2,
2326 :tracker_id => 2,
2309 :status_id => 1,
2327 :status_id => 1,
2310 :subject => 'Test',
2328 :subject => 'Test',
2311 :start_date => '',
2329 :start_date => '',
2312 :due_date => '',
2330 :due_date => '',
2313 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ''}
2331 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ''}
2314 }
2332 }
2315 assert_response :success
2333 assert_response :success
2316 end
2334 end
2317
2335
2318 assert_select_error /Due date cannot be blank/i
2336 assert_select_error /Due date cannot be blank/i
2319 assert_select_error /Bar cannot be blank/i
2337 assert_select_error /Bar cannot be blank/i
2320 end
2338 end
2321
2339
2322 def test_create_should_validate_required_list_fields
2340 def test_create_should_validate_required_list_fields
2323 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'list', :is_for_all => true, :tracker_ids => [1, 2], :multiple => false, :possible_values => ['a', 'b'])
2341 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'list', :is_for_all => true, :tracker_ids => [1, 2], :multiple => false, :possible_values => ['a', 'b'])
2324 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'list', :is_for_all => true, :tracker_ids => [1, 2], :multiple => true, :possible_values => ['a', 'b'])
2342 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'list', :is_for_all => true, :tracker_ids => [1, 2], :multiple => true, :possible_values => ['a', 'b'])
2325 WorkflowPermission.delete_all
2343 WorkflowPermission.delete_all
2326 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf1.id.to_s, :rule => 'required')
2344 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf1.id.to_s, :rule => 'required')
2327 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
2345 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
2328 @request.session[:user_id] = 2
2346 @request.session[:user_id] = 2
2329
2347
2330 assert_no_difference 'Issue.count' do
2348 assert_no_difference 'Issue.count' do
2331 post :create, :project_id => 1, :issue => {
2349 post :create, :project_id => 1, :issue => {
2332 :tracker_id => 2,
2350 :tracker_id => 2,
2333 :status_id => 1,
2351 :status_id => 1,
2334 :subject => 'Test',
2352 :subject => 'Test',
2335 :start_date => '',
2353 :start_date => '',
2336 :due_date => '',
2354 :due_date => '',
2337 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ['']}
2355 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ['']}
2338 }
2356 }
2339 assert_response :success
2357 assert_response :success
2340 end
2358 end
2341
2359
2342 assert_select_error /Foo cannot be blank/i
2360 assert_select_error /Foo cannot be blank/i
2343 assert_select_error /Bar cannot be blank/i
2361 assert_select_error /Bar cannot be blank/i
2344 end
2362 end
2345
2363
2346 def test_create_should_ignore_readonly_fields
2364 def test_create_should_ignore_readonly_fields
2347 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2365 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2348 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2366 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
2349 WorkflowPermission.delete_all
2367 WorkflowPermission.delete_all
2350 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
2368 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
2351 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
2369 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
2352 @request.session[:user_id] = 2
2370 @request.session[:user_id] = 2
2353
2371
2354 assert_difference 'Issue.count' do
2372 assert_difference 'Issue.count' do
2355 post :create, :project_id => 1, :issue => {
2373 post :create, :project_id => 1, :issue => {
2356 :tracker_id => 2,
2374 :tracker_id => 2,
2357 :status_id => 1,
2375 :status_id => 1,
2358 :subject => 'Test',
2376 :subject => 'Test',
2359 :start_date => '2012-07-14',
2377 :start_date => '2012-07-14',
2360 :due_date => '2012-07-16',
2378 :due_date => '2012-07-16',
2361 :custom_field_values => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}
2379 :custom_field_values => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}
2362 }
2380 }
2363 assert_response 302
2381 assert_response 302
2364 end
2382 end
2365
2383
2366 issue = Issue.order('id DESC').first
2384 issue = Issue.order('id DESC').first
2367 assert_equal Date.parse('2012-07-14'), issue.start_date
2385 assert_equal Date.parse('2012-07-14'), issue.start_date
2368 assert_nil issue.due_date
2386 assert_nil issue.due_date
2369 assert_equal 'value1', issue.custom_field_value(cf1)
2387 assert_equal 'value1', issue.custom_field_value(cf1)
2370 assert_nil issue.custom_field_value(cf2)
2388 assert_nil issue.custom_field_value(cf2)
2371 end
2389 end
2372
2390
2373 def test_create_should_ignore_unallowed_trackers
2391 def test_create_should_ignore_unallowed_trackers
2374 role = Role.find(1)
2392 role = Role.find(1)
2375 role.set_permission_trackers :add_issues, [3]
2393 role.set_permission_trackers :add_issues, [3]
2376 role.save!
2394 role.save!
2377 @request.session[:user_id] = 2
2395 @request.session[:user_id] = 2
2378
2396
2379 issue = new_record(Issue) do
2397 issue = new_record(Issue) do
2380 post :create, :project_id => 1, :issue => {
2398 post :create, :project_id => 1, :issue => {
2381 :tracker_id => 1,
2399 :tracker_id => 1,
2382 :status_id => 1,
2400 :status_id => 1,
2383 :subject => 'Test'
2401 :subject => 'Test'
2384 }
2402 }
2385 assert_response 302
2403 assert_response 302
2386 end
2404 end
2387 assert_equal 3, issue.tracker_id
2405 assert_equal 3, issue.tracker_id
2388 end
2406 end
2389
2407
2390 def test_post_create_with_watchers
2408 def test_post_create_with_watchers
2391 @request.session[:user_id] = 2
2409 @request.session[:user_id] = 2
2392 ActionMailer::Base.deliveries.clear
2410 ActionMailer::Base.deliveries.clear
2393
2411
2394 with_settings :notified_events => %w(issue_added) do
2412 with_settings :notified_events => %w(issue_added) do
2395 assert_difference 'Watcher.count', 2 do
2413 assert_difference 'Watcher.count', 2 do
2396 post :create, :project_id => 1,
2414 post :create, :project_id => 1,
2397 :issue => {:tracker_id => 1,
2415 :issue => {:tracker_id => 1,
2398 :subject => 'This is a new issue with watchers',
2416 :subject => 'This is a new issue with watchers',
2399 :description => 'This is the description',
2417 :description => 'This is the description',
2400 :priority_id => 5,
2418 :priority_id => 5,
2401 :watcher_user_ids => ['2', '3']}
2419 :watcher_user_ids => ['2', '3']}
2402 end
2420 end
2403 end
2421 end
2404 issue = Issue.find_by_subject('This is a new issue with watchers')
2422 issue = Issue.find_by_subject('This is a new issue with watchers')
2405 assert_not_nil issue
2423 assert_not_nil issue
2406 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2424 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
2407
2425
2408 # Watchers added
2426 # Watchers added
2409 assert_equal [2, 3], issue.watcher_user_ids.sort
2427 assert_equal [2, 3], issue.watcher_user_ids.sort
2410 assert issue.watched_by?(User.find(3))
2428 assert issue.watched_by?(User.find(3))
2411 # Watchers notified
2429 # Watchers notified
2412 mail = ActionMailer::Base.deliveries.last
2430 mail = ActionMailer::Base.deliveries.last
2413 assert_not_nil mail
2431 assert_not_nil mail
2414 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
2432 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
2415 end
2433 end
2416
2434
2417 def test_post_create_subissue
2435 def test_post_create_subissue
2418 @request.session[:user_id] = 2
2436 @request.session[:user_id] = 2
2419
2437
2420 assert_difference 'Issue.count' do
2438 assert_difference 'Issue.count' do
2421 post :create, :project_id => 1,
2439 post :create, :project_id => 1,
2422 :issue => {:tracker_id => 1,
2440 :issue => {:tracker_id => 1,
2423 :subject => 'This is a child issue',
2441 :subject => 'This is a child issue',
2424 :parent_issue_id => '2'}
2442 :parent_issue_id => '2'}
2425 assert_response 302
2443 assert_response 302
2426 end
2444 end
2427 issue = Issue.order('id DESC').first
2445 issue = Issue.order('id DESC').first
2428 assert_equal Issue.find(2), issue.parent
2446 assert_equal Issue.find(2), issue.parent
2429 end
2447 end
2430
2448
2431 def test_post_create_subissue_with_sharp_parent_id
2449 def test_post_create_subissue_with_sharp_parent_id
2432 @request.session[:user_id] = 2
2450 @request.session[:user_id] = 2
2433
2451
2434 assert_difference 'Issue.count' do
2452 assert_difference 'Issue.count' do
2435 post :create, :project_id => 1,
2453 post :create, :project_id => 1,
2436 :issue => {:tracker_id => 1,
2454 :issue => {:tracker_id => 1,
2437 :subject => 'This is a child issue',
2455 :subject => 'This is a child issue',
2438 :parent_issue_id => '#2'}
2456 :parent_issue_id => '#2'}
2439 assert_response 302
2457 assert_response 302
2440 end
2458 end
2441 issue = Issue.order('id DESC').first
2459 issue = Issue.order('id DESC').first
2442 assert_equal Issue.find(2), issue.parent
2460 assert_equal Issue.find(2), issue.parent
2443 end
2461 end
2444
2462
2445 def test_post_create_subissue_with_non_visible_parent_id_should_not_validate
2463 def test_post_create_subissue_with_non_visible_parent_id_should_not_validate
2446 @request.session[:user_id] = 2
2464 @request.session[:user_id] = 2
2447
2465
2448 assert_no_difference 'Issue.count' do
2466 assert_no_difference 'Issue.count' do
2449 post :create, :project_id => 1,
2467 post :create, :project_id => 1,
2450 :issue => {:tracker_id => 1,
2468 :issue => {:tracker_id => 1,
2451 :subject => 'This is a child issue',
2469 :subject => 'This is a child issue',
2452 :parent_issue_id => '4'}
2470 :parent_issue_id => '4'}
2453
2471
2454 assert_response :success
2472 assert_response :success
2455 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '4'
2473 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '4'
2456 assert_select_error /Parent task is invalid/i
2474 assert_select_error /Parent task is invalid/i
2457 end
2475 end
2458 end
2476 end
2459
2477
2460 def test_post_create_subissue_with_non_numeric_parent_id_should_not_validate
2478 def test_post_create_subissue_with_non_numeric_parent_id_should_not_validate
2461 @request.session[:user_id] = 2
2479 @request.session[:user_id] = 2
2462
2480
2463 assert_no_difference 'Issue.count' do
2481 assert_no_difference 'Issue.count' do
2464 post :create, :project_id => 1,
2482 post :create, :project_id => 1,
2465 :issue => {:tracker_id => 1,
2483 :issue => {:tracker_id => 1,
2466 :subject => 'This is a child issue',
2484 :subject => 'This is a child issue',
2467 :parent_issue_id => '01ABC'}
2485 :parent_issue_id => '01ABC'}
2468
2486
2469 assert_response :success
2487 assert_response :success
2470 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '01ABC'
2488 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '01ABC'
2471 assert_select_error /Parent task is invalid/i
2489 assert_select_error /Parent task is invalid/i
2472 end
2490 end
2473 end
2491 end
2474
2492
2475 def test_post_create_private
2493 def test_post_create_private
2476 @request.session[:user_id] = 2
2494 @request.session[:user_id] = 2
2477
2495
2478 assert_difference 'Issue.count' do
2496 assert_difference 'Issue.count' do
2479 post :create, :project_id => 1,
2497 post :create, :project_id => 1,
2480 :issue => {:tracker_id => 1,
2498 :issue => {:tracker_id => 1,
2481 :subject => 'This is a private issue',
2499 :subject => 'This is a private issue',
2482 :is_private => '1'}
2500 :is_private => '1'}
2483 end
2501 end
2484 issue = Issue.order('id DESC').first
2502 issue = Issue.order('id DESC').first
2485 assert issue.is_private?
2503 assert issue.is_private?
2486 end
2504 end
2487
2505
2488 def test_post_create_private_with_set_own_issues_private_permission
2506 def test_post_create_private_with_set_own_issues_private_permission
2489 role = Role.find(1)
2507 role = Role.find(1)
2490 role.remove_permission! :set_issues_private
2508 role.remove_permission! :set_issues_private
2491 role.add_permission! :set_own_issues_private
2509 role.add_permission! :set_own_issues_private
2492
2510
2493 @request.session[:user_id] = 2
2511 @request.session[:user_id] = 2
2494
2512
2495 assert_difference 'Issue.count' do
2513 assert_difference 'Issue.count' do
2496 post :create, :project_id => 1,
2514 post :create, :project_id => 1,
2497 :issue => {:tracker_id => 1,
2515 :issue => {:tracker_id => 1,
2498 :subject => 'This is a private issue',
2516 :subject => 'This is a private issue',
2499 :is_private => '1'}
2517 :is_private => '1'}
2500 end
2518 end
2501 issue = Issue.order('id DESC').first
2519 issue = Issue.order('id DESC').first
2502 assert issue.is_private?
2520 assert issue.is_private?
2503 end
2521 end
2504
2522
2505 def test_create_without_project_id
2523 def test_create_without_project_id
2506 @request.session[:user_id] = 2
2524 @request.session[:user_id] = 2
2507
2525
2508 assert_difference 'Issue.count' do
2526 assert_difference 'Issue.count' do
2509 post :create,
2527 post :create,
2510 :issue => {:project_id => 3,
2528 :issue => {:project_id => 3,
2511 :tracker_id => 2,
2529 :tracker_id => 2,
2512 :subject => 'Foo'}
2530 :subject => 'Foo'}
2513 assert_response 302
2531 assert_response 302
2514 end
2532 end
2515 issue = Issue.order('id DESC').first
2533 issue = Issue.order('id DESC').first
2516 assert_equal 3, issue.project_id
2534 assert_equal 3, issue.project_id
2517 assert_equal 2, issue.tracker_id
2535 assert_equal 2, issue.tracker_id
2518 end
2536 end
2519
2537
2520 def test_create_without_project_id_and_continue_should_redirect_without_project_id
2538 def test_create_without_project_id_and_continue_should_redirect_without_project_id
2521 @request.session[:user_id] = 2
2539 @request.session[:user_id] = 2
2522
2540
2523 assert_difference 'Issue.count' do
2541 assert_difference 'Issue.count' do
2524 post :create,
2542 post :create,
2525 :issue => {:project_id => 3,
2543 :issue => {:project_id => 3,
2526 :tracker_id => 2,
2544 :tracker_id => 2,
2527 :subject => 'Foo'},
2545 :subject => 'Foo'},
2528 :continue => '1'
2546 :continue => '1'
2529 assert_redirected_to '/issues/new?issue%5Bproject_id%5D=3&issue%5Btracker_id%5D=2'
2547 assert_redirected_to '/issues/new?issue%5Bproject_id%5D=3&issue%5Btracker_id%5D=2'
2530 end
2548 end
2531 end
2549 end
2532
2550
2533 def test_create_without_project_id_should_be_denied_without_permission
2551 def test_create_without_project_id_should_be_denied_without_permission
2534 Role.non_member.remove_permission! :add_issues
2552 Role.non_member.remove_permission! :add_issues
2535 Role.anonymous.remove_permission! :add_issues
2553 Role.anonymous.remove_permission! :add_issues
2536 @request.session[:user_id] = 2
2554 @request.session[:user_id] = 2
2537
2555
2538 assert_no_difference 'Issue.count' do
2556 assert_no_difference 'Issue.count' do
2539 post :create,
2557 post :create,
2540 :issue => {:project_id => 3,
2558 :issue => {:project_id => 3,
2541 :tracker_id => 2,
2559 :tracker_id => 2,
2542 :subject => 'Foo'}
2560 :subject => 'Foo'}
2543 assert_response 422
2561 assert_response 403
2544 end
2562 end
2545 end
2563 end
2546
2564
2547 def test_create_without_project_id_with_failure_should_not_set_project
2565 def test_create_without_project_id_with_failure_should_not_set_project
2548 @request.session[:user_id] = 2
2566 @request.session[:user_id] = 2
2549
2567
2550 post :create,
2568 post :create,
2551 :issue => {:project_id => 3,
2569 :issue => {:project_id => 3,
2552 :tracker_id => 2,
2570 :tracker_id => 2,
2553 :subject => ''}
2571 :subject => ''}
2554 assert_response :success
2572 assert_response :success
2555 # no project menu
2573 # no project menu
2556 assert_select '#main-menu', 0
2574 assert_select '#main-menu', 0
2557 end
2575 end
2558
2576
2559 def test_post_create_should_send_a_notification
2577 def test_post_create_should_send_a_notification
2560 ActionMailer::Base.deliveries.clear
2578 ActionMailer::Base.deliveries.clear
2561 @request.session[:user_id] = 2
2579 @request.session[:user_id] = 2
2562 with_settings :notified_events => %w(issue_added) do
2580 with_settings :notified_events => %w(issue_added) do
2563 assert_difference 'Issue.count' do
2581 assert_difference 'Issue.count' do
2564 post :create, :project_id => 1,
2582 post :create, :project_id => 1,
2565 :issue => {:tracker_id => 3,
2583 :issue => {:tracker_id => 3,
2566 :subject => 'This is the test_new issue',
2584 :subject => 'This is the test_new issue',
2567 :description => 'This is the description',
2585 :description => 'This is the description',
2568 :priority_id => 5,
2586 :priority_id => 5,
2569 :estimated_hours => '',
2587 :estimated_hours => '',
2570 :custom_field_values => {'2' => 'Value for field 2'}}
2588 :custom_field_values => {'2' => 'Value for field 2'}}
2571 end
2589 end
2572 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2590 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2573
2591
2574 assert_equal 1, ActionMailer::Base.deliveries.size
2592 assert_equal 1, ActionMailer::Base.deliveries.size
2575 end
2593 end
2576 end
2594 end
2577
2595
2578 def test_post_create_should_preserve_fields_values_on_validation_failure
2596 def test_post_create_should_preserve_fields_values_on_validation_failure
2579 @request.session[:user_id] = 2
2597 @request.session[:user_id] = 2
2580 post :create, :project_id => 1,
2598 post :create, :project_id => 1,
2581 :issue => {:tracker_id => 1,
2599 :issue => {:tracker_id => 1,
2582 # empty subject
2600 # empty subject
2583 :subject => '',
2601 :subject => '',
2584 :description => 'This is a description',
2602 :description => 'This is a description',
2585 :priority_id => 6,
2603 :priority_id => 6,
2586 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
2604 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
2587 assert_response :success
2605 assert_response :success
2588
2606
2589 assert_select 'textarea[name=?]', 'issue[description]', :text => 'This is a description'
2607 assert_select 'textarea[name=?]', 'issue[description]', :text => 'This is a description'
2590 assert_select 'select[name=?]', 'issue[priority_id]' do
2608 assert_select 'select[name=?]', 'issue[priority_id]' do
2591 assert_select 'option[value="6"][selected=selected]', :text => 'High'
2609 assert_select 'option[value="6"][selected=selected]', :text => 'High'
2592 end
2610 end
2593 # Custom fields
2611 # Custom fields
2594 assert_select 'select[name=?]', 'issue[custom_field_values][1]' do
2612 assert_select 'select[name=?]', 'issue[custom_field_values][1]' do
2595 assert_select 'option[value=Oracle][selected=selected]', :text => 'Oracle'
2613 assert_select 'option[value=Oracle][selected=selected]', :text => 'Oracle'
2596 end
2614 end
2597 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Value for field 2'
2615 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Value for field 2'
2598 end
2616 end
2599
2617
2600 def test_post_create_with_failure_should_preserve_watchers
2618 def test_post_create_with_failure_should_preserve_watchers
2601 assert !User.find(8).member_of?(Project.find(1))
2619 assert !User.find(8).member_of?(Project.find(1))
2602
2620
2603 @request.session[:user_id] = 2
2621 @request.session[:user_id] = 2
2604 post :create, :project_id => 1,
2622 post :create, :project_id => 1,
2605 :issue => {:tracker_id => 1,
2623 :issue => {:tracker_id => 1,
2606 :watcher_user_ids => ['3', '8']}
2624 :watcher_user_ids => ['3', '8']}
2607 assert_response :success
2625 assert_response :success
2608
2626
2609 assert_select 'input[name=?][value="2"]:not(checked)', 'issue[watcher_user_ids][]'
2627 assert_select 'input[name=?][value="2"]:not(checked)', 'issue[watcher_user_ids][]'
2610 assert_select 'input[name=?][value="3"][checked=checked]', 'issue[watcher_user_ids][]'
2628 assert_select 'input[name=?][value="3"][checked=checked]', 'issue[watcher_user_ids][]'
2611 assert_select 'input[name=?][value="8"][checked=checked]', 'issue[watcher_user_ids][]'
2629 assert_select 'input[name=?][value="8"][checked=checked]', 'issue[watcher_user_ids][]'
2612 end
2630 end
2613
2631
2614 def test_post_create_should_ignore_non_safe_attributes
2632 def test_post_create_should_ignore_non_safe_attributes
2615 @request.session[:user_id] = 2
2633 @request.session[:user_id] = 2
2616 assert_nothing_raised do
2634 assert_nothing_raised do
2617 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
2635 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
2618 end
2636 end
2619 end
2637 end
2620
2638
2621 def test_post_create_with_attachment
2639 def test_post_create_with_attachment
2622 set_tmp_attachments_directory
2640 set_tmp_attachments_directory
2623 @request.session[:user_id] = 2
2641 @request.session[:user_id] = 2
2624
2642
2625 assert_difference 'Issue.count' do
2643 assert_difference 'Issue.count' do
2626 assert_difference 'Attachment.count' do
2644 assert_difference 'Attachment.count' do
2627 assert_no_difference 'Journal.count' do
2645 assert_no_difference 'Journal.count' do
2628 post :create, :project_id => 1,
2646 post :create, :project_id => 1,
2629 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2647 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2630 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2648 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2631 end
2649 end
2632 end
2650 end
2633 end
2651 end
2634
2652
2635 issue = Issue.order('id DESC').first
2653 issue = Issue.order('id DESC').first
2636 attachment = Attachment.order('id DESC').first
2654 attachment = Attachment.order('id DESC').first
2637
2655
2638 assert_equal issue, attachment.container
2656 assert_equal issue, attachment.container
2639 assert_equal 2, attachment.author_id
2657 assert_equal 2, attachment.author_id
2640 assert_equal 'testfile.txt', attachment.filename
2658 assert_equal 'testfile.txt', attachment.filename
2641 assert_equal 'text/plain', attachment.content_type
2659 assert_equal 'text/plain', attachment.content_type
2642 assert_equal 'test file', attachment.description
2660 assert_equal 'test file', attachment.description
2643 assert_equal 59, attachment.filesize
2661 assert_equal 59, attachment.filesize
2644 assert File.exists?(attachment.diskfile)
2662 assert File.exists?(attachment.diskfile)
2645 assert_equal 59, File.size(attachment.diskfile)
2663 assert_equal 59, File.size(attachment.diskfile)
2646 end
2664 end
2647
2665
2648 def test_post_create_with_attachment_should_notify_with_attachments
2666 def test_post_create_with_attachment_should_notify_with_attachments
2649 ActionMailer::Base.deliveries.clear
2667 ActionMailer::Base.deliveries.clear
2650 set_tmp_attachments_directory
2668 set_tmp_attachments_directory
2651 @request.session[:user_id] = 2
2669 @request.session[:user_id] = 2
2652
2670
2653 with_settings :notified_events => %w(issue_added) do
2671 with_settings :notified_events => %w(issue_added) do
2654 assert_difference 'Issue.count' do
2672 assert_difference 'Issue.count' do
2655 post :create, :project_id => 1,
2673 post :create, :project_id => 1,
2656 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2674 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2657 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2675 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2658 end
2676 end
2659 end
2677 end
2660
2678
2661 assert_not_nil ActionMailer::Base.deliveries.last
2679 assert_not_nil ActionMailer::Base.deliveries.last
2662 assert_select_email do
2680 assert_select_email do
2663 assert_select 'a[href^=?]', 'http://localhost:3000/attachments/download', 'testfile.txt'
2681 assert_select 'a[href^=?]', 'http://localhost:3000/attachments/download', 'testfile.txt'
2664 end
2682 end
2665 end
2683 end
2666
2684
2667 def test_post_create_with_failure_should_save_attachments
2685 def test_post_create_with_failure_should_save_attachments
2668 set_tmp_attachments_directory
2686 set_tmp_attachments_directory
2669 @request.session[:user_id] = 2
2687 @request.session[:user_id] = 2
2670
2688
2671 assert_no_difference 'Issue.count' do
2689 assert_no_difference 'Issue.count' do
2672 assert_difference 'Attachment.count' do
2690 assert_difference 'Attachment.count' do
2673 post :create, :project_id => 1,
2691 post :create, :project_id => 1,
2674 :issue => { :tracker_id => '1', :subject => '' },
2692 :issue => { :tracker_id => '1', :subject => '' },
2675 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2693 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2676 assert_response :success
2694 assert_response :success
2677 end
2695 end
2678 end
2696 end
2679
2697
2680 attachment = Attachment.order('id DESC').first
2698 attachment = Attachment.order('id DESC').first
2681 assert_equal 'testfile.txt', attachment.filename
2699 assert_equal 'testfile.txt', attachment.filename
2682 assert File.exists?(attachment.diskfile)
2700 assert File.exists?(attachment.diskfile)
2683 assert_nil attachment.container
2701 assert_nil attachment.container
2684
2702
2685 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
2703 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
2686 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
2704 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
2687 end
2705 end
2688
2706
2689 def test_post_create_with_failure_should_keep_saved_attachments
2707 def test_post_create_with_failure_should_keep_saved_attachments
2690 set_tmp_attachments_directory
2708 set_tmp_attachments_directory
2691 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2709 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2692 @request.session[:user_id] = 2
2710 @request.session[:user_id] = 2
2693
2711
2694 assert_no_difference 'Issue.count' do
2712 assert_no_difference 'Issue.count' do
2695 assert_no_difference 'Attachment.count' do
2713 assert_no_difference 'Attachment.count' do
2696 post :create, :project_id => 1,
2714 post :create, :project_id => 1,
2697 :issue => { :tracker_id => '1', :subject => '' },
2715 :issue => { :tracker_id => '1', :subject => '' },
2698 :attachments => {'p0' => {'token' => attachment.token}}
2716 :attachments => {'p0' => {'token' => attachment.token}}
2699 assert_response :success
2717 assert_response :success
2700 end
2718 end
2701 end
2719 end
2702
2720
2703 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
2721 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
2704 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
2722 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
2705 end
2723 end
2706
2724
2707 def test_post_create_should_attach_saved_attachments
2725 def test_post_create_should_attach_saved_attachments
2708 set_tmp_attachments_directory
2726 set_tmp_attachments_directory
2709 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2727 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2710 @request.session[:user_id] = 2
2728 @request.session[:user_id] = 2
2711
2729
2712 assert_difference 'Issue.count' do
2730 assert_difference 'Issue.count' do
2713 assert_no_difference 'Attachment.count' do
2731 assert_no_difference 'Attachment.count' do
2714 post :create, :project_id => 1,
2732 post :create, :project_id => 1,
2715 :issue => { :tracker_id => '1', :subject => 'Saved attachments' },
2733 :issue => { :tracker_id => '1', :subject => 'Saved attachments' },
2716 :attachments => {'p0' => {'token' => attachment.token}}
2734 :attachments => {'p0' => {'token' => attachment.token}}
2717 assert_response 302
2735 assert_response 302
2718 end
2736 end
2719 end
2737 end
2720
2738
2721 issue = Issue.order('id DESC').first
2739 issue = Issue.order('id DESC').first
2722 assert_equal 1, issue.attachments.count
2740 assert_equal 1, issue.attachments.count
2723
2741
2724 attachment.reload
2742 attachment.reload
2725 assert_equal issue, attachment.container
2743 assert_equal issue, attachment.container
2726 end
2744 end
2727
2745
2728 def setup_without_workflow_privilege
2746 def setup_without_workflow_privilege
2729 WorkflowTransition.where(["role_id = ?", Role.anonymous.id]).delete_all
2747 WorkflowTransition.where(["role_id = ?", Role.anonymous.id]).delete_all
2730 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2748 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2731 end
2749 end
2732 private :setup_without_workflow_privilege
2750 private :setup_without_workflow_privilege
2733
2751
2734 test "without workflow privilege #new should propose default status only" do
2752 test "without workflow privilege #new should propose default status only" do
2735 setup_without_workflow_privilege
2753 setup_without_workflow_privilege
2736 get :new, :project_id => 1
2754 get :new, :project_id => 1
2737 assert_response :success
2755 assert_response :success
2738
2756
2739 assert_select 'select[name=?]', 'issue[status_id]' do
2757 assert_select 'select[name=?]', 'issue[status_id]' do
2740 assert_select 'option', 1
2758 assert_select 'option', 1
2741 assert_select 'option[value=?][selected=selected]', '1'
2759 assert_select 'option[value=?][selected=selected]', '1'
2742 end
2760 end
2743 end
2761 end
2744
2762
2745 test "without workflow privilege #create should accept default status" do
2763 test "without workflow privilege #create should accept default status" do
2746 setup_without_workflow_privilege
2764 setup_without_workflow_privilege
2747 assert_difference 'Issue.count' do
2765 assert_difference 'Issue.count' do
2748 post :create, :project_id => 1,
2766 post :create, :project_id => 1,
2749 :issue => {:tracker_id => 1,
2767 :issue => {:tracker_id => 1,
2750 :subject => 'This is an issue',
2768 :subject => 'This is an issue',
2751 :status_id => 1}
2769 :status_id => 1}
2752 end
2770 end
2753 issue = Issue.order('id').last
2771 issue = Issue.order('id').last
2754 assert_not_nil issue.default_status
2772 assert_not_nil issue.default_status
2755 assert_equal issue.default_status, issue.status
2773 assert_equal issue.default_status, issue.status
2756 end
2774 end
2757
2775
2758 test "without workflow privilege #create should ignore unauthorized status" do
2776 test "without workflow privilege #create should ignore unauthorized status" do
2759 setup_without_workflow_privilege
2777 setup_without_workflow_privilege
2760 assert_difference 'Issue.count' do
2778 assert_difference 'Issue.count' do
2761 post :create, :project_id => 1,
2779 post :create, :project_id => 1,
2762 :issue => {:tracker_id => 1,
2780 :issue => {:tracker_id => 1,
2763 :subject => 'This is an issue',
2781 :subject => 'This is an issue',
2764 :status_id => 3}
2782 :status_id => 3}
2765 end
2783 end
2766 issue = Issue.order('id').last
2784 issue = Issue.order('id').last
2767 assert_not_nil issue.default_status
2785 assert_not_nil issue.default_status
2768 assert_equal issue.default_status, issue.status
2786 assert_equal issue.default_status, issue.status
2769 end
2787 end
2770
2788
2771 test "without workflow privilege #update should ignore status change" do
2789 test "without workflow privilege #update should ignore status change" do
2772 setup_without_workflow_privilege
2790 setup_without_workflow_privilege
2773 assert_difference 'Journal.count' do
2791 assert_difference 'Journal.count' do
2774 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2792 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2775 end
2793 end
2776 assert_equal 1, Issue.find(1).status_id
2794 assert_equal 1, Issue.find(1).status_id
2777 end
2795 end
2778
2796
2779 test "without workflow privilege #update ignore attributes changes" do
2797 test "without workflow privilege #update ignore attributes changes" do
2780 setup_without_workflow_privilege
2798 setup_without_workflow_privilege
2781 assert_difference 'Journal.count' do
2799 assert_difference 'Journal.count' do
2782 put :update, :id => 1,
2800 put :update, :id => 1,
2783 :issue => {:subject => 'changed', :assigned_to_id => 2,
2801 :issue => {:subject => 'changed', :assigned_to_id => 2,
2784 :notes => 'just trying'}
2802 :notes => 'just trying'}
2785 end
2803 end
2786 issue = Issue.find(1)
2804 issue = Issue.find(1)
2787 assert_equal "Cannot print recipes", issue.subject
2805 assert_equal "Cannot print recipes", issue.subject
2788 assert_nil issue.assigned_to
2806 assert_nil issue.assigned_to
2789 end
2807 end
2790
2808
2791 def setup_with_workflow_privilege
2809 def setup_with_workflow_privilege
2792 WorkflowTransition.where(["role_id = ?", Role.anonymous.id]).delete_all
2810 WorkflowTransition.where(["role_id = ?", Role.anonymous.id]).delete_all
2793 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
2811 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
2794 :old_status_id => 1, :new_status_id => 3)
2812 :old_status_id => 1, :new_status_id => 3)
2795 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
2813 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
2796 :old_status_id => 1, :new_status_id => 4)
2814 :old_status_id => 1, :new_status_id => 4)
2797 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2815 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2798 end
2816 end
2799 private :setup_with_workflow_privilege
2817 private :setup_with_workflow_privilege
2800
2818
2801 def setup_with_workflow_privilege_and_edit_issues_permission
2819 def setup_with_workflow_privilege_and_edit_issues_permission
2802 setup_with_workflow_privilege
2820 setup_with_workflow_privilege
2803 Role.anonymous.add_permission! :add_issues, :edit_issues
2821 Role.anonymous.add_permission! :add_issues, :edit_issues
2804 end
2822 end
2805 private :setup_with_workflow_privilege_and_edit_issues_permission
2823 private :setup_with_workflow_privilege_and_edit_issues_permission
2806
2824
2807 test "with workflow privilege and :edit_issues permission should accept authorized status" do
2825 test "with workflow privilege and :edit_issues permission should accept authorized status" do
2808 setup_with_workflow_privilege_and_edit_issues_permission
2826 setup_with_workflow_privilege_and_edit_issues_permission
2809 assert_difference 'Journal.count' do
2827 assert_difference 'Journal.count' do
2810 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2828 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2811 end
2829 end
2812 assert_equal 3, Issue.find(1).status_id
2830 assert_equal 3, Issue.find(1).status_id
2813 end
2831 end
2814
2832
2815 test "with workflow privilege and :edit_issues permission should ignore unauthorized status" do
2833 test "with workflow privilege and :edit_issues permission should ignore unauthorized status" do
2816 setup_with_workflow_privilege_and_edit_issues_permission
2834 setup_with_workflow_privilege_and_edit_issues_permission
2817 assert_difference 'Journal.count' do
2835 assert_difference 'Journal.count' do
2818 put :update, :id => 1, :issue => {:status_id => 2, :notes => 'just trying'}
2836 put :update, :id => 1, :issue => {:status_id => 2, :notes => 'just trying'}
2819 end
2837 end
2820 assert_equal 1, Issue.find(1).status_id
2838 assert_equal 1, Issue.find(1).status_id
2821 end
2839 end
2822
2840
2823 test "with workflow privilege and :edit_issues permission should accept authorized attributes changes" do
2841 test "with workflow privilege and :edit_issues permission should accept authorized attributes changes" do
2824 setup_with_workflow_privilege_and_edit_issues_permission
2842 setup_with_workflow_privilege_and_edit_issues_permission
2825 assert_difference 'Journal.count' do
2843 assert_difference 'Journal.count' do
2826 put :update, :id => 1,
2844 put :update, :id => 1,
2827 :issue => {:subject => 'changed', :assigned_to_id => 2,
2845 :issue => {:subject => 'changed', :assigned_to_id => 2,
2828 :notes => 'just trying'}
2846 :notes => 'just trying'}
2829 end
2847 end
2830 issue = Issue.find(1)
2848 issue = Issue.find(1)
2831 assert_equal "changed", issue.subject
2849 assert_equal "changed", issue.subject
2832 assert_equal 2, issue.assigned_to_id
2850 assert_equal 2, issue.assigned_to_id
2833 end
2851 end
2834
2852
2835 def test_new_as_copy
2853 def test_new_as_copy
2836 orig = Issue.find(1)
2854 orig = Issue.find(1)
2837 @request.session[:user_id] = 2
2855 @request.session[:user_id] = 2
2838
2856
2839 get :new, :project_id => 1, :copy_from => orig.id
2857 get :new, :project_id => 1, :copy_from => orig.id
2840 assert_response :success
2858 assert_response :success
2841
2859
2842 assert_select 'form[id=issue-form][action="/projects/ecookbook/issues"]' do
2860 assert_select 'form[id=issue-form][action="/projects/ecookbook/issues"]' do
2843 assert_select 'select[name=?]', 'issue[project_id]' do
2861 assert_select 'select[name=?]', 'issue[project_id]' do
2844 assert_select 'option[value="1"][selected=selected]', :text => 'eCookbook'
2862 assert_select 'option[value="1"][selected=selected]', :text => 'eCookbook'
2845 assert_select 'option[value="2"]:not([selected])', :text => 'OnlineStore'
2863 assert_select 'option[value="2"]:not([selected])', :text => 'OnlineStore'
2846 end
2864 end
2847 assert_select 'input[name=?][value=?]', 'issue[subject]', orig.subject
2865 assert_select 'input[name=?][value=?]', 'issue[subject]', orig.subject
2848 assert_select 'input[name=copy_from][value="1"]'
2866 assert_select 'input[name=copy_from][value="1"]'
2849 end
2867 end
2850 end
2868 end
2851
2869
2852 def test_new_as_copy_without_add_issues_permission_should_not_propose_current_project_as_target
2870 def test_new_as_copy_without_add_issues_permission_should_not_propose_current_project_as_target
2853 user = setup_user_with_copy_but_not_add_permission
2871 user = setup_user_with_copy_but_not_add_permission
2854
2872
2855 @request.session[:user_id] = user.id
2873 @request.session[:user_id] = user.id
2856 get :new, :project_id => 1, :copy_from => 1
2874 get :new, :project_id => 1, :copy_from => 1
2857 assert_response :success
2875 assert_response :success
2858
2876
2859 assert_select 'select[name=?]', 'issue[project_id]' do
2877 assert_select 'select[name=?]', 'issue[project_id]' do
2860 assert_select 'option[value="1"]', 0
2878 assert_select 'option[value="1"]', 0
2861 assert_select 'option[value="2"]', :text => 'OnlineStore'
2879 assert_select 'option[value="2"]', :text => 'OnlineStore'
2862 end
2880 end
2863 end
2881 end
2864
2882
2865 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
2883 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
2866 @request.session[:user_id] = 2
2884 @request.session[:user_id] = 2
2867 issue = Issue.find(3)
2885 issue = Issue.find(3)
2868 assert issue.attachments.count > 0
2886 assert issue.attachments.count > 0
2869 get :new, :project_id => 1, :copy_from => 3
2887 get :new, :project_id => 1, :copy_from => 3
2870
2888
2871 assert_select 'input[name=copy_attachments][type=checkbox][checked=checked][value="1"]'
2889 assert_select 'input[name=copy_attachments][type=checkbox][checked=checked][value="1"]'
2872 end
2890 end
2873
2891
2874 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
2892 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
2875 @request.session[:user_id] = 2
2893 @request.session[:user_id] = 2
2876 issue = Issue.find(3)
2894 issue = Issue.find(3)
2877 issue.attachments.delete_all
2895 issue.attachments.delete_all
2878 get :new, :project_id => 1, :copy_from => 3
2896 get :new, :project_id => 1, :copy_from => 3
2879
2897
2880 assert_select 'input[name=copy_attachments]', 0
2898 assert_select 'input[name=copy_attachments]', 0
2881 end
2899 end
2882
2900
2883 def test_new_as_copy_should_preserve_parent_id
2901 def test_new_as_copy_should_preserve_parent_id
2884 @request.session[:user_id] = 2
2902 @request.session[:user_id] = 2
2885 issue = Issue.generate!(:parent_issue_id => 2)
2903 issue = Issue.generate!(:parent_issue_id => 2)
2886 get :new, :project_id => 1, :copy_from => issue.id
2904 get :new, :project_id => 1, :copy_from => issue.id
2887
2905
2888 assert_select 'input[name=?][value="2"]', 'issue[parent_issue_id]'
2906 assert_select 'input[name=?][value="2"]', 'issue[parent_issue_id]'
2889 end
2907 end
2890
2908
2891 def test_new_as_copy_with_subtasks_should_show_copy_subtasks_checkbox
2909 def test_new_as_copy_with_subtasks_should_show_copy_subtasks_checkbox
2892 @request.session[:user_id] = 2
2910 @request.session[:user_id] = 2
2893 issue = Issue.generate_with_descendants!
2911 issue = Issue.generate_with_descendants!
2894 get :new, :project_id => 1, :copy_from => issue.id
2912 get :new, :project_id => 1, :copy_from => issue.id
2895
2913
2896 assert_select 'input[type=checkbox][name=copy_subtasks][checked=checked][value="1"]'
2914 assert_select 'input[type=checkbox][name=copy_subtasks][checked=checked][value="1"]'
2897 end
2915 end
2898
2916
2899 def test_new_as_copy_with_invalid_issue_should_respond_with_404
2917 def test_new_as_copy_with_invalid_issue_should_respond_with_404
2900 @request.session[:user_id] = 2
2918 @request.session[:user_id] = 2
2901 get :new, :project_id => 1, :copy_from => 99999
2919 get :new, :project_id => 1, :copy_from => 99999
2902 assert_response 404
2920 assert_response 404
2903 end
2921 end
2904
2922
2905 def test_create_as_copy_on_different_project
2923 def test_create_as_copy_on_different_project
2906 @request.session[:user_id] = 2
2924 @request.session[:user_id] = 2
2907 assert_difference 'Issue.count' do
2925 assert_difference 'Issue.count' do
2908 post :create, :project_id => 1, :copy_from => 1,
2926 post :create, :project_id => 1, :copy_from => 1,
2909 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2927 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2910 end
2928 end
2911 issue = Issue.order('id DESC').first
2929 issue = Issue.order('id DESC').first
2912 assert_redirected_to "/issues/#{issue.id}"
2930 assert_redirected_to "/issues/#{issue.id}"
2913
2931
2914 assert_equal 2, issue.project_id
2932 assert_equal 2, issue.project_id
2915 assert_equal 3, issue.tracker_id
2933 assert_equal 3, issue.tracker_id
2916 assert_equal 'Copy', issue.subject
2934 assert_equal 'Copy', issue.subject
2917 end
2935 end
2918
2936
2919 def test_create_as_copy_should_allow_status_to_be_set_to_default
2937 def test_create_as_copy_should_allow_status_to_be_set_to_default
2920 copied = Issue.generate! :status_id => 2
2938 copied = Issue.generate! :status_id => 2
2921 assert_equal 2, copied.reload.status_id
2939 assert_equal 2, copied.reload.status_id
2922
2940
2923 @request.session[:user_id] = 2
2941 @request.session[:user_id] = 2
2924 assert_difference 'Issue.count' do
2942 assert_difference 'Issue.count' do
2925 post :create, :project_id => 1, :copy_from => copied.id,
2943 post :create, :project_id => 1, :copy_from => copied.id,
2926 :issue => {:project_id => '1', :tracker_id => '1', :status_id => '1'},
2944 :issue => {:project_id => '1', :tracker_id => '1', :status_id => '1'},
2927 :was_default_status => '1'
2945 :was_default_status => '1'
2928 end
2946 end
2929 issue = Issue.order('id DESC').first
2947 issue = Issue.order('id DESC').first
2930 assert_equal 1, issue.status_id
2948 assert_equal 1, issue.status_id
2931 end
2949 end
2932
2950
2933 def test_create_as_copy_should_copy_attachments
2951 def test_create_as_copy_should_copy_attachments
2934 @request.session[:user_id] = 2
2952 @request.session[:user_id] = 2
2935 issue = Issue.find(3)
2953 issue = Issue.find(3)
2936 count = issue.attachments.count
2954 count = issue.attachments.count
2937 assert count > 0
2955 assert count > 0
2938 assert_difference 'Issue.count' do
2956 assert_difference 'Issue.count' do
2939 assert_difference 'Attachment.count', count do
2957 assert_difference 'Attachment.count', count do
2940 post :create, :project_id => 1, :copy_from => 3,
2958 post :create, :project_id => 1, :copy_from => 3,
2941 :issue => {:project_id => '1', :tracker_id => '3',
2959 :issue => {:project_id => '1', :tracker_id => '3',
2942 :status_id => '1', :subject => 'Copy with attachments'},
2960 :status_id => '1', :subject => 'Copy with attachments'},
2943 :copy_attachments => '1'
2961 :copy_attachments => '1'
2944 end
2962 end
2945 end
2963 end
2946 copy = Issue.order('id DESC').first
2964 copy = Issue.order('id DESC').first
2947 assert_equal count, copy.attachments.count
2965 assert_equal count, copy.attachments.count
2948 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
2966 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
2949 end
2967 end
2950
2968
2951 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
2969 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
2952 @request.session[:user_id] = 2
2970 @request.session[:user_id] = 2
2953 issue = Issue.find(3)
2971 issue = Issue.find(3)
2954 count = issue.attachments.count
2972 count = issue.attachments.count
2955 assert count > 0
2973 assert count > 0
2956 assert_difference 'Issue.count' do
2974 assert_difference 'Issue.count' do
2957 assert_no_difference 'Attachment.count' do
2975 assert_no_difference 'Attachment.count' do
2958 post :create, :project_id => 1, :copy_from => 3,
2976 post :create, :project_id => 1, :copy_from => 3,
2959 :issue => {:project_id => '1', :tracker_id => '3',
2977 :issue => {:project_id => '1', :tracker_id => '3',
2960 :status_id => '1', :subject => 'Copy with attachments'}
2978 :status_id => '1', :subject => 'Copy with attachments'}
2961 end
2979 end
2962 end
2980 end
2963 copy = Issue.order('id DESC').first
2981 copy = Issue.order('id DESC').first
2964 assert_equal 0, copy.attachments.count
2982 assert_equal 0, copy.attachments.count
2965 end
2983 end
2966
2984
2967 def test_create_as_copy_with_attachments_should_also_add_new_files
2985 def test_create_as_copy_with_attachments_should_also_add_new_files
2968 @request.session[:user_id] = 2
2986 @request.session[:user_id] = 2
2969 issue = Issue.find(3)
2987 issue = Issue.find(3)
2970 count = issue.attachments.count
2988 count = issue.attachments.count
2971 assert count > 0
2989 assert count > 0
2972 assert_difference 'Issue.count' do
2990 assert_difference 'Issue.count' do
2973 assert_difference 'Attachment.count', count + 1 do
2991 assert_difference 'Attachment.count', count + 1 do
2974 post :create, :project_id => 1, :copy_from => 3,
2992 post :create, :project_id => 1, :copy_from => 3,
2975 :issue => {:project_id => '1', :tracker_id => '3',
2993 :issue => {:project_id => '1', :tracker_id => '3',
2976 :status_id => '1', :subject => 'Copy with attachments'},
2994 :status_id => '1', :subject => 'Copy with attachments'},
2977 :copy_attachments => '1',
2995 :copy_attachments => '1',
2978 :attachments => {'1' =>
2996 :attachments => {'1' =>
2979 {'file' => uploaded_test_file('testfile.txt', 'text/plain'),
2997 {'file' => uploaded_test_file('testfile.txt', 'text/plain'),
2980 'description' => 'test file'}}
2998 'description' => 'test file'}}
2981 end
2999 end
2982 end
3000 end
2983 copy = Issue.order('id DESC').first
3001 copy = Issue.order('id DESC').first
2984 assert_equal count + 1, copy.attachments.count
3002 assert_equal count + 1, copy.attachments.count
2985 end
3003 end
2986
3004
2987 def test_create_as_copy_should_add_relation_with_copied_issue
3005 def test_create_as_copy_should_add_relation_with_copied_issue
2988 @request.session[:user_id] = 2
3006 @request.session[:user_id] = 2
2989 assert_difference 'Issue.count' do
3007 assert_difference 'Issue.count' do
2990 assert_difference 'IssueRelation.count' do
3008 assert_difference 'IssueRelation.count' do
2991 post :create, :project_id => 1, :copy_from => 1, :link_copy => '1',
3009 post :create, :project_id => 1, :copy_from => 1, :link_copy => '1',
2992 :issue => {:project_id => '1', :tracker_id => '3',
3010 :issue => {:project_id => '1', :tracker_id => '3',
2993 :status_id => '1', :subject => 'Copy'}
3011 :status_id => '1', :subject => 'Copy'}
2994 end
3012 end
2995 end
3013 end
2996 copy = Issue.order('id DESC').first
3014 copy = Issue.order('id DESC').first
2997 assert_equal 1, copy.relations.size
3015 assert_equal 1, copy.relations.size
2998 end
3016 end
2999
3017
3000 def test_create_as_copy_should_allow_not_to_add_relation_with_copied_issue
3018 def test_create_as_copy_should_allow_not_to_add_relation_with_copied_issue
3001 @request.session[:user_id] = 2
3019 @request.session[:user_id] = 2
3002 assert_difference 'Issue.count' do
3020 assert_difference 'Issue.count' do
3003 assert_no_difference 'IssueRelation.count' do
3021 assert_no_difference 'IssueRelation.count' do
3004 post :create, :project_id => 1, :copy_from => 1,
3022 post :create, :project_id => 1, :copy_from => 1,
3005 :issue => {:subject => 'Copy'}
3023 :issue => {:subject => 'Copy'}
3006 end
3024 end
3007 end
3025 end
3008 end
3026 end
3009
3027
3010 def test_create_as_copy_should_always_add_relation_with_copied_issue_by_setting
3028 def test_create_as_copy_should_always_add_relation_with_copied_issue_by_setting
3011 with_settings :link_copied_issue => 'yes' do
3029 with_settings :link_copied_issue => 'yes' do
3012 @request.session[:user_id] = 2
3030 @request.session[:user_id] = 2
3013 assert_difference 'Issue.count' do
3031 assert_difference 'Issue.count' do
3014 assert_difference 'IssueRelation.count' do
3032 assert_difference 'IssueRelation.count' do
3015 post :create, :project_id => 1, :copy_from => 1,
3033 post :create, :project_id => 1, :copy_from => 1,
3016 :issue => {:subject => 'Copy'}
3034 :issue => {:subject => 'Copy'}
3017 end
3035 end
3018 end
3036 end
3019 end
3037 end
3020 end
3038 end
3021
3039
3022 def test_create_as_copy_should_never_add_relation_with_copied_issue_by_setting
3040 def test_create_as_copy_should_never_add_relation_with_copied_issue_by_setting
3023 with_settings :link_copied_issue => 'no' do
3041 with_settings :link_copied_issue => 'no' do
3024 @request.session[:user_id] = 2
3042 @request.session[:user_id] = 2
3025 assert_difference 'Issue.count' do
3043 assert_difference 'Issue.count' do
3026 assert_no_difference 'IssueRelation.count' do
3044 assert_no_difference 'IssueRelation.count' do
3027 post :create, :project_id => 1, :copy_from => 1, :link_copy => '1',
3045 post :create, :project_id => 1, :copy_from => 1, :link_copy => '1',
3028 :issue => {:subject => 'Copy'}
3046 :issue => {:subject => 'Copy'}
3029 end
3047 end
3030 end
3048 end
3031 end
3049 end
3032 end
3050 end
3033
3051
3034 def test_create_as_copy_should_copy_subtasks
3052 def test_create_as_copy_should_copy_subtasks
3035 @request.session[:user_id] = 2
3053 @request.session[:user_id] = 2
3036 issue = Issue.generate_with_descendants!
3054 issue = Issue.generate_with_descendants!
3037 count = issue.descendants.count
3055 count = issue.descendants.count
3038 assert_difference 'Issue.count', count + 1 do
3056 assert_difference 'Issue.count', count + 1 do
3039 post :create, :project_id => 1, :copy_from => issue.id,
3057 post :create, :project_id => 1, :copy_from => issue.id,
3040 :issue => {:project_id => '1', :tracker_id => '3',
3058 :issue => {:project_id => '1', :tracker_id => '3',
3041 :status_id => '1', :subject => 'Copy with subtasks'},
3059 :status_id => '1', :subject => 'Copy with subtasks'},
3042 :copy_subtasks => '1'
3060 :copy_subtasks => '1'
3043 end
3061 end
3044 copy = Issue.where(:parent_id => nil).order('id DESC').first
3062 copy = Issue.where(:parent_id => nil).order('id DESC').first
3045 assert_equal count, copy.descendants.count
3063 assert_equal count, copy.descendants.count
3046 assert_equal issue.descendants.map(&:subject).sort, copy.descendants.map(&:subject).sort
3064 assert_equal issue.descendants.map(&:subject).sort, copy.descendants.map(&:subject).sort
3047 end
3065 end
3048
3066
3049 def test_create_as_copy_to_a_different_project_should_copy_subtask_custom_fields
3067 def test_create_as_copy_to_a_different_project_should_copy_subtask_custom_fields
3050 issue = Issue.generate! {|i| i.custom_field_values = {'2' => 'Foo'}}
3068 issue = Issue.generate! {|i| i.custom_field_values = {'2' => 'Foo'}}
3051 child = Issue.generate!(:parent_issue_id => issue.id) {|i| i.custom_field_values = {'2' => 'Bar'}}
3069 child = Issue.generate!(:parent_issue_id => issue.id) {|i| i.custom_field_values = {'2' => 'Bar'}}
3052 @request.session[:user_id] = 1
3070 @request.session[:user_id] = 1
3053
3071
3054 assert_difference 'Issue.count', 2 do
3072 assert_difference 'Issue.count', 2 do
3055 post :create, :project_id => 'ecookbook', :copy_from => issue.id,
3073 post :create, :project_id => 'ecookbook', :copy_from => issue.id,
3056 :issue => {:project_id => '2', :tracker_id => 1, :status_id => '1',
3074 :issue => {:project_id => '2', :tracker_id => 1, :status_id => '1',
3057 :subject => 'Copy with subtasks', :custom_field_values => {'2' => 'Foo'}},
3075 :subject => 'Copy with subtasks', :custom_field_values => {'2' => 'Foo'}},
3058 :copy_subtasks => '1'
3076 :copy_subtasks => '1'
3059 end
3077 end
3060
3078
3061 child_copy, issue_copy = Issue.order(:id => :desc).limit(2).to_a
3079 child_copy, issue_copy = Issue.order(:id => :desc).limit(2).to_a
3062 assert_equal 2, issue_copy.project_id
3080 assert_equal 2, issue_copy.project_id
3063 assert_equal 'Foo', issue_copy.custom_field_value(2)
3081 assert_equal 'Foo', issue_copy.custom_field_value(2)
3064 assert_equal 'Bar', child_copy.custom_field_value(2)
3082 assert_equal 'Bar', child_copy.custom_field_value(2)
3065 end
3083 end
3066
3084
3067 def test_create_as_copy_without_copy_subtasks_option_should_not_copy_subtasks
3085 def test_create_as_copy_without_copy_subtasks_option_should_not_copy_subtasks
3068 @request.session[:user_id] = 2
3086 @request.session[:user_id] = 2
3069 issue = Issue.generate_with_descendants!
3087 issue = Issue.generate_with_descendants!
3070 assert_difference 'Issue.count', 1 do
3088 assert_difference 'Issue.count', 1 do
3071 post :create, :project_id => 1, :copy_from => 3,
3089 post :create, :project_id => 1, :copy_from => 3,
3072 :issue => {:project_id => '1', :tracker_id => '3',
3090 :issue => {:project_id => '1', :tracker_id => '3',
3073 :status_id => '1', :subject => 'Copy with subtasks'}
3091 :status_id => '1', :subject => 'Copy with subtasks'}
3074 end
3092 end
3075 copy = Issue.where(:parent_id => nil).order('id DESC').first
3093 copy = Issue.where(:parent_id => nil).order('id DESC').first
3076 assert_equal 0, copy.descendants.count
3094 assert_equal 0, copy.descendants.count
3077 end
3095 end
3078
3096
3079 def test_create_as_copy_with_failure
3097 def test_create_as_copy_with_failure
3080 @request.session[:user_id] = 2
3098 @request.session[:user_id] = 2
3081 post :create, :project_id => 1, :copy_from => 1,
3099 post :create, :project_id => 1, :copy_from => 1,
3082 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
3100 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
3083
3101
3084 assert_response :success
3102 assert_response :success
3085
3103
3086 assert_select 'form#issue-form[action="/projects/ecookbook/issues"]' do
3104 assert_select 'form#issue-form[action="/projects/ecookbook/issues"]' do
3087 assert_select 'select[name=?]', 'issue[project_id]' do
3105 assert_select 'select[name=?]', 'issue[project_id]' do
3088 assert_select 'option[value="1"]:not([selected])', :text => 'eCookbook'
3106 assert_select 'option[value="1"]:not([selected])', :text => 'eCookbook'
3089 assert_select 'option[value="2"][selected=selected]', :text => 'OnlineStore'
3107 assert_select 'option[value="2"][selected=selected]', :text => 'OnlineStore'
3090 end
3108 end
3091 assert_select 'input[name=copy_from][value="1"]'
3109 assert_select 'input[name=copy_from][value="1"]'
3092 end
3110 end
3093 end
3111 end
3094
3112
3095 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
3113 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
3096 @request.session[:user_id] = 2
3114 @request.session[:user_id] = 2
3097 assert !User.find(2).member_of?(Project.find(4))
3115 assert !User.find(2).member_of?(Project.find(4))
3098
3116
3099 assert_difference 'Issue.count' do
3117 assert_difference 'Issue.count' do
3100 post :create, :project_id => 1, :copy_from => 1,
3118 post :create, :project_id => 1, :copy_from => 1,
3101 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
3119 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
3102 end
3120 end
3103 issue = Issue.order('id DESC').first
3121 issue = Issue.order('id DESC').first
3104 assert_equal 1, issue.project_id
3122 assert_equal 1, issue.project_id
3105 end
3123 end
3106
3124
3107 def test_get_edit
3125 def test_get_edit
3108 @request.session[:user_id] = 2
3126 @request.session[:user_id] = 2
3109 get :edit, :id => 1
3127 get :edit, :id => 1
3110 assert_response :success
3128 assert_response :success
3111
3129
3112 # Be sure we don't display inactive IssuePriorities
3130 # Be sure we don't display inactive IssuePriorities
3113 assert ! IssuePriority.find(15).active?
3131 assert ! IssuePriority.find(15).active?
3114 assert_select 'select[name=?]', 'issue[priority_id]' do
3132 assert_select 'select[name=?]', 'issue[priority_id]' do
3115 assert_select 'option[value="15"]', 0
3133 assert_select 'option[value="15"]', 0
3116 end
3134 end
3117 end
3135 end
3118
3136
3119 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
3137 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
3120 @request.session[:user_id] = 2
3138 @request.session[:user_id] = 2
3121 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
3139 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
3122
3140
3123 get :edit, :id => 1
3141 get :edit, :id => 1
3124 assert_select 'input[name=?]', 'time_entry[hours]'
3142 assert_select 'input[name=?]', 'time_entry[hours]'
3125 end
3143 end
3126
3144
3127 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
3145 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
3128 @request.session[:user_id] = 2
3146 @request.session[:user_id] = 2
3129 Role.find_by_name('Manager').remove_permission! :log_time
3147 Role.find_by_name('Manager').remove_permission! :log_time
3130
3148
3131 get :edit, :id => 1
3149 get :edit, :id => 1
3132 assert_select 'input[name=?]', 'time_entry[hours]', 0
3150 assert_select 'input[name=?]', 'time_entry[hours]', 0
3133 end
3151 end
3134
3152
3135 def test_get_edit_with_params
3153 def test_get_edit_with_params
3136 @request.session[:user_id] = 2
3154 @request.session[:user_id] = 2
3137 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
3155 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
3138 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => 10 }
3156 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => 10 }
3139 assert_response :success
3157 assert_response :success
3140
3158
3141 assert_select 'select[name=?]', 'issue[status_id]' do
3159 assert_select 'select[name=?]', 'issue[status_id]' do
3142 assert_select 'option[value="5"][selected=selected]', :text => 'Closed'
3160 assert_select 'option[value="5"][selected=selected]', :text => 'Closed'
3143 end
3161 end
3144
3162
3145 assert_select 'select[name=?]', 'issue[priority_id]' do
3163 assert_select 'select[name=?]', 'issue[priority_id]' do
3146 assert_select 'option[value="7"][selected=selected]', :text => 'Urgent'
3164 assert_select 'option[value="7"][selected=selected]', :text => 'Urgent'
3147 end
3165 end
3148
3166
3149 assert_select 'input[name=?][value="2.5"]', 'time_entry[hours]'
3167 assert_select 'input[name=?][value="2.5"]', 'time_entry[hours]'
3150 assert_select 'select[name=?]', 'time_entry[activity_id]' do
3168 assert_select 'select[name=?]', 'time_entry[activity_id]' do
3151 assert_select 'option[value="10"][selected=selected]', :text => 'Development'
3169 assert_select 'option[value="10"][selected=selected]', :text => 'Development'
3152 end
3170 end
3153 assert_select 'input[name=?][value=test_get_edit_with_params]', 'time_entry[comments]'
3171 assert_select 'input[name=?][value=test_get_edit_with_params]', 'time_entry[comments]'
3154 end
3172 end
3155
3173
3156 def test_get_edit_with_multi_custom_field
3174 def test_get_edit_with_multi_custom_field
3157 field = CustomField.find(1)
3175 field = CustomField.find(1)
3158 field.update_attribute :multiple, true
3176 field.update_attribute :multiple, true
3159 issue = Issue.find(1)
3177 issue = Issue.find(1)
3160 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
3178 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
3161 issue.save!
3179 issue.save!
3162
3180
3163 @request.session[:user_id] = 2
3181 @request.session[:user_id] = 2
3164 get :edit, :id => 1
3182 get :edit, :id => 1
3165 assert_response :success
3183 assert_response :success
3166
3184
3167 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
3185 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
3168 assert_select 'option', 3
3186 assert_select 'option', 3
3169 assert_select 'option[value=MySQL][selected=selected]'
3187 assert_select 'option[value=MySQL][selected=selected]'
3170 assert_select 'option[value=Oracle][selected=selected]'
3188 assert_select 'option[value=Oracle][selected=selected]'
3171 assert_select 'option[value=PostgreSQL]:not([selected])'
3189 assert_select 'option[value=PostgreSQL]:not([selected])'
3172 end
3190 end
3173 end
3191 end
3174
3192
3175 def test_update_form_for_existing_issue
3193 def test_update_form_for_existing_issue
3176 @request.session[:user_id] = 2
3194 @request.session[:user_id] = 2
3177 xhr :patch, :edit, :id => 1,
3195 xhr :patch, :edit, :id => 1,
3178 :issue => {:tracker_id => 2,
3196 :issue => {:tracker_id => 2,
3179 :subject => 'This is the test_new issue',
3197 :subject => 'This is the test_new issue',
3180 :description => 'This is the description',
3198 :description => 'This is the description',
3181 :priority_id => 5}
3199 :priority_id => 5}
3182 assert_response :success
3200 assert_response :success
3183 assert_equal 'text/javascript', response.content_type
3201 assert_equal 'text/javascript', response.content_type
3184
3202
3185 assert_include 'This is the test_new issue', response.body
3203 assert_include 'This is the test_new issue', response.body
3186 end
3204 end
3187
3205
3188 def test_update_form_for_existing_issue_should_keep_issue_author
3206 def test_update_form_for_existing_issue_should_keep_issue_author
3189 @request.session[:user_id] = 3
3207 @request.session[:user_id] = 3
3190 patch :edit, :id => 1, :issue => {:subject => 'Changed'}
3208 patch :edit, :id => 1, :issue => {:subject => 'Changed'}
3191 assert_response :success
3209 assert_response :success
3192
3210
3193 assert_equal User.find(2), Issue.find(1).author
3211 assert_equal User.find(2), Issue.find(1).author
3194 end
3212 end
3195
3213
3196 def test_update_form_for_existing_issue_should_propose_transitions_based_on_initial_status
3214 def test_update_form_for_existing_issue_should_propose_transitions_based_on_initial_status
3197 @request.session[:user_id] = 2
3215 @request.session[:user_id] = 2
3198 WorkflowTransition.delete_all
3216 WorkflowTransition.delete_all
3199 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
3217 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
3200 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
3218 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
3201 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 5, :new_status_id => 4)
3219 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 5, :new_status_id => 4)
3202
3220
3203 patch :edit, :id => 2,
3221 patch :edit, :id => 2,
3204 :issue => {:tracker_id => 2,
3222 :issue => {:tracker_id => 2,
3205 :status_id => 5,
3223 :status_id => 5,
3206 :subject => 'This is an issue'}
3224 :subject => 'This is an issue'}
3207
3225
3208 assert_select 'select[name=?]', 'issue[status_id]' do
3226 assert_select 'select[name=?]', 'issue[status_id]' do
3209 assert_select 'option[value="1"]'
3227 assert_select 'option[value="1"]'
3210 assert_select 'option[value="2"]'
3228 assert_select 'option[value="2"]'
3211 assert_select 'option[value="5"][selected=selected]'
3229 assert_select 'option[value="5"][selected=selected]'
3212 assert_select 'option', 3
3230 assert_select 'option', 3
3213 end
3231 end
3214 end
3232 end
3215
3233
3216 def test_update_form_for_existing_issue_with_project_change
3234 def test_update_form_for_existing_issue_with_project_change
3217 @request.session[:user_id] = 2
3235 @request.session[:user_id] = 2
3218 patch :edit, :id => 1,
3236 patch :edit, :id => 1,
3219 :issue => {:project_id => 2,
3237 :issue => {:project_id => 2,
3220 :tracker_id => 2,
3238 :tracker_id => 2,
3221 :subject => 'This is the test_new issue',
3239 :subject => 'This is the test_new issue',
3222 :description => 'This is the description',
3240 :description => 'This is the description',
3223 :priority_id => 5}
3241 :priority_id => 5}
3224 assert_response :success
3242 assert_response :success
3225 assert_select 'select[name=?]', 'issue[project_id]' do
3243 assert_select 'select[name=?]', 'issue[project_id]' do
3226 assert_select 'option[value="2"][selected=selected]'
3244 assert_select 'option[value="2"][selected=selected]'
3227 end
3245 end
3228 assert_select 'select[name=?]', 'issue[tracker_id]' do
3246 assert_select 'select[name=?]', 'issue[tracker_id]' do
3229 assert_select 'option[value="2"][selected=selected]'
3247 assert_select 'option[value="2"][selected=selected]'
3230 end
3248 end
3231 assert_select 'input[name=?][value=?]', 'issue[subject]', 'This is the test_new issue'
3249 assert_select 'input[name=?][value=?]', 'issue[subject]', 'This is the test_new issue'
3232 end
3250 end
3233
3251
3234 def test_update_form_should_keep_category_with_same_when_changing_project
3252 def test_update_form_should_keep_category_with_same_when_changing_project
3235 source = Project.generate!
3253 source = Project.generate!
3236 target = Project.generate!
3254 target = Project.generate!
3237 source_category = IssueCategory.create!(:name => 'Foo', :project => source)
3255 source_category = IssueCategory.create!(:name => 'Foo', :project => source)
3238 target_category = IssueCategory.create!(:name => 'Foo', :project => target)
3256 target_category = IssueCategory.create!(:name => 'Foo', :project => target)
3239 issue = Issue.generate!(:project => source, :category => source_category)
3257 issue = Issue.generate!(:project => source, :category => source_category)
3240
3258
3241 @request.session[:user_id] = 1
3259 @request.session[:user_id] = 1
3242 patch :edit, :id => issue.id,
3260 patch :edit, :id => issue.id,
3243 :issue => {:project_id => target.id, :category_id => source_category.id}
3261 :issue => {:project_id => target.id, :category_id => source_category.id}
3244 assert_response :success
3262 assert_response :success
3245
3263
3246 assert_select 'select[name=?]', 'issue[category_id]' do
3264 assert_select 'select[name=?]', 'issue[category_id]' do
3247 assert_select 'option[value=?][selected=selected]', target_category.id.to_s
3265 assert_select 'option[value=?][selected=selected]', target_category.id.to_s
3248 end
3266 end
3249 end
3267 end
3250
3268
3251 def test_update_form_should_propose_default_status_for_existing_issue
3269 def test_update_form_should_propose_default_status_for_existing_issue
3252 @request.session[:user_id] = 2
3270 @request.session[:user_id] = 2
3253 WorkflowTransition.delete_all
3271 WorkflowTransition.delete_all
3254 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3)
3272 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3)
3255
3273
3256 patch :edit, :id => 2
3274 patch :edit, :id => 2
3257 assert_response :success
3275 assert_response :success
3258 assert_select 'select[name=?]', 'issue[status_id]' do
3276 assert_select 'select[name=?]', 'issue[status_id]' do
3259 assert_select 'option[value="2"]'
3277 assert_select 'option[value="2"]'
3260 assert_select 'option[value="3"]'
3278 assert_select 'option[value="3"]'
3261 assert_select 'option', 2
3279 assert_select 'option', 2
3262 end
3280 end
3263 end
3281 end
3264
3282
3265 def test_put_update_without_custom_fields_param
3283 def test_put_update_without_custom_fields_param
3266 @request.session[:user_id] = 2
3284 @request.session[:user_id] = 2
3267
3285
3268 issue = Issue.find(1)
3286 issue = Issue.find(1)
3269 assert_equal '125', issue.custom_value_for(2).value
3287 assert_equal '125', issue.custom_value_for(2).value
3270
3288
3271 assert_difference('Journal.count') do
3289 assert_difference('Journal.count') do
3272 assert_difference('JournalDetail.count') do
3290 assert_difference('JournalDetail.count') do
3273 put :update, :id => 1, :issue => {:subject => 'New subject'}
3291 put :update, :id => 1, :issue => {:subject => 'New subject'}
3274 end
3292 end
3275 end
3293 end
3276 assert_redirected_to :action => 'show', :id => '1'
3294 assert_redirected_to :action => 'show', :id => '1'
3277 issue.reload
3295 issue.reload
3278 assert_equal 'New subject', issue.subject
3296 assert_equal 'New subject', issue.subject
3279 # Make sure custom fields were not cleared
3297 # Make sure custom fields were not cleared
3280 assert_equal '125', issue.custom_value_for(2).value
3298 assert_equal '125', issue.custom_value_for(2).value
3281 end
3299 end
3282
3300
3283 def test_put_update_with_project_change
3301 def test_put_update_with_project_change
3284 @request.session[:user_id] = 2
3302 @request.session[:user_id] = 2
3285 ActionMailer::Base.deliveries.clear
3303 ActionMailer::Base.deliveries.clear
3286
3304
3287 with_settings :notified_events => %w(issue_updated) do
3305 with_settings :notified_events => %w(issue_updated) do
3288 assert_difference('Journal.count') do
3306 assert_difference('Journal.count') do
3289 assert_difference('JournalDetail.count', 3) do
3307 assert_difference('JournalDetail.count', 3) do
3290 put :update, :id => 1, :issue => {:project_id => '2',
3308 put :update, :id => 1, :issue => {:project_id => '2',
3291 :tracker_id => '1', # no change
3309 :tracker_id => '1', # no change
3292 :priority_id => '6',
3310 :priority_id => '6',
3293 :category_id => '3'
3311 :category_id => '3'
3294 }
3312 }
3295 end
3313 end
3296 end
3314 end
3297 end
3315 end
3298 assert_redirected_to :action => 'show', :id => '1'
3316 assert_redirected_to :action => 'show', :id => '1'
3299 issue = Issue.find(1)
3317 issue = Issue.find(1)
3300 assert_equal 2, issue.project_id
3318 assert_equal 2, issue.project_id
3301 assert_equal 1, issue.tracker_id
3319 assert_equal 1, issue.tracker_id
3302 assert_equal 6, issue.priority_id
3320 assert_equal 6, issue.priority_id
3303 assert_equal 3, issue.category_id
3321 assert_equal 3, issue.category_id
3304
3322
3305 mail = ActionMailer::Base.deliveries.last
3323 mail = ActionMailer::Base.deliveries.last
3306 assert_not_nil mail
3324 assert_not_nil mail
3307 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
3325 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
3308 assert_mail_body_match "Project changed from eCookbook to OnlineStore", mail
3326 assert_mail_body_match "Project changed from eCookbook to OnlineStore", mail
3309 end
3327 end
3310
3328
3311 def test_put_update_trying_to_move_issue_to_project_without_tracker_should_not_error
3329 def test_put_update_trying_to_move_issue_to_project_without_tracker_should_not_error
3312 target = Project.generate!(:tracker_ids => [])
3330 target = Project.generate!(:tracker_ids => [])
3313 assert target.trackers.empty?
3331 assert target.trackers.empty?
3314 issue = Issue.generate!
3332 issue = Issue.generate!
3315 @request.session[:user_id] = 1
3333 @request.session[:user_id] = 1
3316
3334
3317 put :update, :id => issue.id, :issue => {:project_id => target.id}
3335 put :update, :id => issue.id, :issue => {:project_id => target.id}
3318 assert_response 302
3336 assert_response 302
3319 end
3337 end
3320
3338
3321 def test_put_update_with_tracker_change
3339 def test_put_update_with_tracker_change
3322 @request.session[:user_id] = 2
3340 @request.session[:user_id] = 2
3323 ActionMailer::Base.deliveries.clear
3341 ActionMailer::Base.deliveries.clear
3324
3342
3325 with_settings :notified_events => %w(issue_updated) do
3343 with_settings :notified_events => %w(issue_updated) do
3326 assert_difference('Journal.count') do
3344 assert_difference('Journal.count') do
3327 assert_difference('JournalDetail.count', 2) do
3345 assert_difference('JournalDetail.count', 2) do
3328 put :update, :id => 1, :issue => {:project_id => '1',
3346 put :update, :id => 1, :issue => {:project_id => '1',
3329 :tracker_id => '2',
3347 :tracker_id => '2',
3330 :priority_id => '6'
3348 :priority_id => '6'
3331 }
3349 }
3332 end
3350 end
3333 end
3351 end
3334 end
3352 end
3335 assert_redirected_to :action => 'show', :id => '1'
3353 assert_redirected_to :action => 'show', :id => '1'
3336 issue = Issue.find(1)
3354 issue = Issue.find(1)
3337 assert_equal 1, issue.project_id
3355 assert_equal 1, issue.project_id
3338 assert_equal 2, issue.tracker_id
3356 assert_equal 2, issue.tracker_id
3339 assert_equal 6, issue.priority_id
3357 assert_equal 6, issue.priority_id
3340 assert_equal 1, issue.category_id
3358 assert_equal 1, issue.category_id
3341
3359
3342 mail = ActionMailer::Base.deliveries.last
3360 mail = ActionMailer::Base.deliveries.last
3343 assert_not_nil mail
3361 assert_not_nil mail
3344 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
3362 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
3345 assert_mail_body_match "Tracker changed from Bug to Feature request", mail
3363 assert_mail_body_match "Tracker changed from Bug to Feature request", mail
3346 end
3364 end
3347
3365
3348 def test_put_update_with_custom_field_change
3366 def test_put_update_with_custom_field_change
3349 @request.session[:user_id] = 2
3367 @request.session[:user_id] = 2
3350 issue = Issue.find(1)
3368 issue = Issue.find(1)
3351 assert_equal '125', issue.custom_value_for(2).value
3369 assert_equal '125', issue.custom_value_for(2).value
3352
3370
3353 with_settings :notified_events => %w(issue_updated) do
3371 with_settings :notified_events => %w(issue_updated) do
3354 assert_difference('Journal.count') do
3372 assert_difference('Journal.count') do
3355 assert_difference('JournalDetail.count', 3) do
3373 assert_difference('JournalDetail.count', 3) do
3356 put :update, :id => 1, :issue => {:subject => 'Custom field change',
3374 put :update, :id => 1, :issue => {:subject => 'Custom field change',
3357 :priority_id => '6',
3375 :priority_id => '6',
3358 :category_id => '1', # no change
3376 :category_id => '1', # no change
3359 :custom_field_values => { '2' => 'New custom value' }
3377 :custom_field_values => { '2' => 'New custom value' }
3360 }
3378 }
3361 end
3379 end
3362 end
3380 end
3363 end
3381 end
3364 assert_redirected_to :action => 'show', :id => '1'
3382 assert_redirected_to :action => 'show', :id => '1'
3365 issue.reload
3383 issue.reload
3366 assert_equal 'New custom value', issue.custom_value_for(2).value
3384 assert_equal 'New custom value', issue.custom_value_for(2).value
3367
3385
3368 mail = ActionMailer::Base.deliveries.last
3386 mail = ActionMailer::Base.deliveries.last
3369 assert_not_nil mail
3387 assert_not_nil mail
3370 assert_mail_body_match "Searchable field changed from 125 to New custom value", mail
3388 assert_mail_body_match "Searchable field changed from 125 to New custom value", mail
3371 end
3389 end
3372
3390
3373 def test_put_update_with_multi_custom_field_change
3391 def test_put_update_with_multi_custom_field_change
3374 field = CustomField.find(1)
3392 field = CustomField.find(1)
3375 field.update_attribute :multiple, true
3393 field.update_attribute :multiple, true
3376 issue = Issue.find(1)
3394 issue = Issue.find(1)
3377 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
3395 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
3378 issue.save!
3396 issue.save!
3379
3397
3380 @request.session[:user_id] = 2
3398 @request.session[:user_id] = 2
3381 assert_difference('Journal.count') do
3399 assert_difference('Journal.count') do
3382 assert_difference('JournalDetail.count', 3) do
3400 assert_difference('JournalDetail.count', 3) do
3383 put :update, :id => 1,
3401 put :update, :id => 1,
3384 :issue => {
3402 :issue => {
3385 :subject => 'Custom field change',
3403 :subject => 'Custom field change',
3386 :custom_field_values => { '1' => ['', 'Oracle', 'PostgreSQL'] }
3404 :custom_field_values => { '1' => ['', 'Oracle', 'PostgreSQL'] }
3387 }
3405 }
3388 end
3406 end
3389 end
3407 end
3390 assert_redirected_to :action => 'show', :id => '1'
3408 assert_redirected_to :action => 'show', :id => '1'
3391 assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
3409 assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
3392 end
3410 end
3393
3411
3394 def test_put_update_with_status_and_assignee_change
3412 def test_put_update_with_status_and_assignee_change
3395 issue = Issue.find(1)
3413 issue = Issue.find(1)
3396 assert_equal 1, issue.status_id
3414 assert_equal 1, issue.status_id
3397 @request.session[:user_id] = 2
3415 @request.session[:user_id] = 2
3398
3416
3399 with_settings :notified_events => %w(issue_updated) do
3417 with_settings :notified_events => %w(issue_updated) do
3400 assert_difference('TimeEntry.count', 0) do
3418 assert_difference('TimeEntry.count', 0) do
3401 put :update,
3419 put :update,
3402 :id => 1,
3420 :id => 1,
3403 :issue => { :status_id => 2, :assigned_to_id => 3, :notes => 'Assigned to dlopper' },
3421 :issue => { :status_id => 2, :assigned_to_id => 3, :notes => 'Assigned to dlopper' },
3404 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
3422 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
3405 end
3423 end
3406 end
3424 end
3407 assert_redirected_to :action => 'show', :id => '1'
3425 assert_redirected_to :action => 'show', :id => '1'
3408 issue.reload
3426 issue.reload
3409 assert_equal 2, issue.status_id
3427 assert_equal 2, issue.status_id
3410 j = Journal.order('id DESC').first
3428 j = Journal.order('id DESC').first
3411 assert_equal 'Assigned to dlopper', j.notes
3429 assert_equal 'Assigned to dlopper', j.notes
3412 assert_equal 2, j.details.size
3430 assert_equal 2, j.details.size
3413
3431
3414 mail = ActionMailer::Base.deliveries.last
3432 mail = ActionMailer::Base.deliveries.last
3415 assert_mail_body_match "Status changed from New to Assigned", mail
3433 assert_mail_body_match "Status changed from New to Assigned", mail
3416 # subject should contain the new status
3434 # subject should contain the new status
3417 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
3435 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
3418 end
3436 end
3419
3437
3420 def test_put_update_with_note_only
3438 def test_put_update_with_note_only
3421 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
3439 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
3422
3440
3423 with_settings :notified_events => %w(issue_updated) do
3441 with_settings :notified_events => %w(issue_updated) do
3424 # anonymous user
3442 # anonymous user
3425 put :update,
3443 put :update,
3426 :id => 1,
3444 :id => 1,
3427 :issue => { :notes => notes }
3445 :issue => { :notes => notes }
3428 end
3446 end
3429 assert_redirected_to :action => 'show', :id => '1'
3447 assert_redirected_to :action => 'show', :id => '1'
3430 j = Journal.order('id DESC').first
3448 j = Journal.order('id DESC').first
3431 assert_equal notes, j.notes
3449 assert_equal notes, j.notes
3432 assert_equal 0, j.details.size
3450 assert_equal 0, j.details.size
3433 assert_equal User.anonymous, j.user
3451 assert_equal User.anonymous, j.user
3434
3452
3435 mail = ActionMailer::Base.deliveries.last
3453 mail = ActionMailer::Base.deliveries.last
3436 assert_mail_body_match notes, mail
3454 assert_mail_body_match notes, mail
3437 end
3455 end
3438
3456
3439 def test_put_update_with_private_note_only
3457 def test_put_update_with_private_note_only
3440 notes = 'Private note'
3458 notes = 'Private note'
3441 @request.session[:user_id] = 2
3459 @request.session[:user_id] = 2
3442
3460
3443 assert_difference 'Journal.count' do
3461 assert_difference 'Journal.count' do
3444 put :update, :id => 1, :issue => {:notes => notes, :private_notes => '1'}
3462 put :update, :id => 1, :issue => {:notes => notes, :private_notes => '1'}
3445 assert_redirected_to :action => 'show', :id => '1'
3463 assert_redirected_to :action => 'show', :id => '1'
3446 end
3464 end
3447
3465
3448 j = Journal.order('id DESC').first
3466 j = Journal.order('id DESC').first
3449 assert_equal notes, j.notes
3467 assert_equal notes, j.notes
3450 assert_equal true, j.private_notes
3468 assert_equal true, j.private_notes
3451 end
3469 end
3452
3470
3453 def test_put_update_with_private_note_and_changes
3471 def test_put_update_with_private_note_and_changes
3454 notes = 'Private note'
3472 notes = 'Private note'
3455 @request.session[:user_id] = 2
3473 @request.session[:user_id] = 2
3456
3474
3457 assert_difference 'Journal.count', 2 do
3475 assert_difference 'Journal.count', 2 do
3458 put :update, :id => 1, :issue => {:subject => 'New subject', :notes => notes, :private_notes => '1'}
3476 put :update, :id => 1, :issue => {:subject => 'New subject', :notes => notes, :private_notes => '1'}
3459 assert_redirected_to :action => 'show', :id => '1'
3477 assert_redirected_to :action => 'show', :id => '1'
3460 end
3478 end
3461
3479
3462 j = Journal.order('id DESC').first
3480 j = Journal.order('id DESC').first
3463 assert_equal notes, j.notes
3481 assert_equal notes, j.notes
3464 assert_equal true, j.private_notes
3482 assert_equal true, j.private_notes
3465 assert_equal 0, j.details.count
3483 assert_equal 0, j.details.count
3466
3484
3467 j = Journal.order('id DESC').offset(1).first
3485 j = Journal.order('id DESC').offset(1).first
3468 assert_nil j.notes
3486 assert_nil j.notes
3469 assert_equal false, j.private_notes
3487 assert_equal false, j.private_notes
3470 assert_equal 1, j.details.count
3488 assert_equal 1, j.details.count
3471 end
3489 end
3472
3490
3473 def test_put_update_with_note_and_spent_time
3491 def test_put_update_with_note_and_spent_time
3474 @request.session[:user_id] = 2
3492 @request.session[:user_id] = 2
3475 spent_hours_before = Issue.find(1).spent_hours
3493 spent_hours_before = Issue.find(1).spent_hours
3476 assert_difference('TimeEntry.count') do
3494 assert_difference('TimeEntry.count') do
3477 put :update,
3495 put :update,
3478 :id => 1,
3496 :id => 1,
3479 :issue => { :notes => '2.5 hours added' },
3497 :issue => { :notes => '2.5 hours added' },
3480 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
3498 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
3481 end
3499 end
3482 assert_redirected_to :action => 'show', :id => '1'
3500 assert_redirected_to :action => 'show', :id => '1'
3483
3501
3484 issue = Issue.find(1)
3502 issue = Issue.find(1)
3485
3503
3486 j = Journal.order('id DESC').first
3504 j = Journal.order('id DESC').first
3487 assert_equal '2.5 hours added', j.notes
3505 assert_equal '2.5 hours added', j.notes
3488 assert_equal 0, j.details.size
3506 assert_equal 0, j.details.size
3489
3507
3490 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
3508 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
3491 assert_not_nil t
3509 assert_not_nil t
3492 assert_equal 2.5, t.hours
3510 assert_equal 2.5, t.hours
3493 assert_equal spent_hours_before + 2.5, issue.spent_hours
3511 assert_equal spent_hours_before + 2.5, issue.spent_hours
3494 end
3512 end
3495
3513
3496 def test_put_update_should_preserve_parent_issue_even_if_not_visible
3514 def test_put_update_should_preserve_parent_issue_even_if_not_visible
3497 parent = Issue.generate!(:project_id => 1, :is_private => true)
3515 parent = Issue.generate!(:project_id => 1, :is_private => true)
3498 issue = Issue.generate!(:parent_issue_id => parent.id)
3516 issue = Issue.generate!(:parent_issue_id => parent.id)
3499 assert !parent.visible?(User.find(3))
3517 assert !parent.visible?(User.find(3))
3500 @request.session[:user_id] = 3
3518 @request.session[:user_id] = 3
3501
3519
3502 get :edit, :id => issue.id
3520 get :edit, :id => issue.id
3503 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', parent.id.to_s
3521 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', parent.id.to_s
3504
3522
3505 put :update, :id => issue.id, :issue => {:subject => 'New subject', :parent_issue_id => parent.id.to_s}
3523 put :update, :id => issue.id, :issue => {:subject => 'New subject', :parent_issue_id => parent.id.to_s}
3506 assert_response 302
3524 assert_response 302
3507 assert_equal parent, issue.parent
3525 assert_equal parent, issue.parent
3508 end
3526 end
3509
3527
3510 def test_put_update_with_attachment_only
3528 def test_put_update_with_attachment_only
3511 set_tmp_attachments_directory
3529 set_tmp_attachments_directory
3512
3530
3513 # Delete all fixtured journals, a race condition can occur causing the wrong
3531 # Delete all fixtured journals, a race condition can occur causing the wrong
3514 # journal to get fetched in the next find.
3532 # journal to get fetched in the next find.
3515 Journal.delete_all
3533 Journal.delete_all
3516
3534
3517 with_settings :notified_events => %w(issue_updated) do
3535 with_settings :notified_events => %w(issue_updated) do
3518 # anonymous user
3536 # anonymous user
3519 assert_difference 'Attachment.count' do
3537 assert_difference 'Attachment.count' do
3520 put :update, :id => 1,
3538 put :update, :id => 1,
3521 :issue => {:notes => ''},
3539 :issue => {:notes => ''},
3522 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
3540 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
3523 end
3541 end
3524 end
3542 end
3525
3543
3526 assert_redirected_to :action => 'show', :id => '1'
3544 assert_redirected_to :action => 'show', :id => '1'
3527 j = Issue.find(1).journals.reorder('id DESC').first
3545 j = Issue.find(1).journals.reorder('id DESC').first
3528 assert j.notes.blank?
3546 assert j.notes.blank?
3529 assert_equal 1, j.details.size
3547 assert_equal 1, j.details.size
3530 assert_equal 'testfile.txt', j.details.first.value
3548 assert_equal 'testfile.txt', j.details.first.value
3531 assert_equal User.anonymous, j.user
3549 assert_equal User.anonymous, j.user
3532
3550
3533 attachment = Attachment.order('id DESC').first
3551 attachment = Attachment.order('id DESC').first
3534 assert_equal Issue.find(1), attachment.container
3552 assert_equal Issue.find(1), attachment.container
3535 assert_equal User.anonymous, attachment.author
3553 assert_equal User.anonymous, attachment.author
3536 assert_equal 'testfile.txt', attachment.filename
3554 assert_equal 'testfile.txt', attachment.filename
3537 assert_equal 'text/plain', attachment.content_type
3555 assert_equal 'text/plain', attachment.content_type
3538 assert_equal 'test file', attachment.description
3556 assert_equal 'test file', attachment.description
3539 assert_equal 59, attachment.filesize
3557 assert_equal 59, attachment.filesize
3540 assert File.exists?(attachment.diskfile)
3558 assert File.exists?(attachment.diskfile)
3541 assert_equal 59, File.size(attachment.diskfile)
3559 assert_equal 59, File.size(attachment.diskfile)
3542
3560
3543 mail = ActionMailer::Base.deliveries.last
3561 mail = ActionMailer::Base.deliveries.last
3544 assert_mail_body_match 'testfile.txt', mail
3562 assert_mail_body_match 'testfile.txt', mail
3545 end
3563 end
3546
3564
3547 def test_put_update_with_failure_should_save_attachments
3565 def test_put_update_with_failure_should_save_attachments
3548 set_tmp_attachments_directory
3566 set_tmp_attachments_directory
3549 @request.session[:user_id] = 2
3567 @request.session[:user_id] = 2
3550
3568
3551 assert_no_difference 'Journal.count' do
3569 assert_no_difference 'Journal.count' do
3552 assert_difference 'Attachment.count' do
3570 assert_difference 'Attachment.count' do
3553 put :update, :id => 1,
3571 put :update, :id => 1,
3554 :issue => { :subject => '' },
3572 :issue => { :subject => '' },
3555 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
3573 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
3556 assert_response :success
3574 assert_response :success
3557 end
3575 end
3558 end
3576 end
3559
3577
3560 attachment = Attachment.order('id DESC').first
3578 attachment = Attachment.order('id DESC').first
3561 assert_equal 'testfile.txt', attachment.filename
3579 assert_equal 'testfile.txt', attachment.filename
3562 assert File.exists?(attachment.diskfile)
3580 assert File.exists?(attachment.diskfile)
3563 assert_nil attachment.container
3581 assert_nil attachment.container
3564
3582
3565 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
3583 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
3566 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
3584 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
3567 end
3585 end
3568
3586
3569 def test_put_update_with_failure_should_keep_saved_attachments
3587 def test_put_update_with_failure_should_keep_saved_attachments
3570 set_tmp_attachments_directory
3588 set_tmp_attachments_directory
3571 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
3589 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
3572 @request.session[:user_id] = 2
3590 @request.session[:user_id] = 2
3573
3591
3574 assert_no_difference 'Journal.count' do
3592 assert_no_difference 'Journal.count' do
3575 assert_no_difference 'Attachment.count' do
3593 assert_no_difference 'Attachment.count' do
3576 put :update, :id => 1,
3594 put :update, :id => 1,
3577 :issue => { :subject => '' },
3595 :issue => { :subject => '' },
3578 :attachments => {'p0' => {'token' => attachment.token}}
3596 :attachments => {'p0' => {'token' => attachment.token}}
3579 assert_response :success
3597 assert_response :success
3580 end
3598 end
3581 end
3599 end
3582
3600
3583 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
3601 assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
3584 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
3602 assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
3585 end
3603 end
3586
3604
3587 def test_put_update_should_attach_saved_attachments
3605 def test_put_update_should_attach_saved_attachments
3588 set_tmp_attachments_directory
3606 set_tmp_attachments_directory
3589 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
3607 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
3590 @request.session[:user_id] = 2
3608 @request.session[:user_id] = 2
3591
3609
3592 assert_difference 'Journal.count' do
3610 assert_difference 'Journal.count' do
3593 assert_difference 'JournalDetail.count' do
3611 assert_difference 'JournalDetail.count' do
3594 assert_no_difference 'Attachment.count' do
3612 assert_no_difference 'Attachment.count' do
3595 put :update, :id => 1,
3613 put :update, :id => 1,
3596 :issue => {:notes => 'Attachment added'},
3614 :issue => {:notes => 'Attachment added'},
3597 :attachments => {'p0' => {'token' => attachment.token}}
3615 :attachments => {'p0' => {'token' => attachment.token}}
3598 assert_redirected_to '/issues/1'
3616 assert_redirected_to '/issues/1'
3599 end
3617 end
3600 end
3618 end
3601 end
3619 end
3602
3620
3603 attachment.reload
3621 attachment.reload
3604 assert_equal Issue.find(1), attachment.container
3622 assert_equal Issue.find(1), attachment.container
3605
3623
3606 journal = Journal.order('id DESC').first
3624 journal = Journal.order('id DESC').first
3607 assert_equal 1, journal.details.size
3625 assert_equal 1, journal.details.size
3608 assert_equal 'testfile.txt', journal.details.first.value
3626 assert_equal 'testfile.txt', journal.details.first.value
3609 end
3627 end
3610
3628
3611 def test_put_update_with_attachment_that_fails_to_save
3629 def test_put_update_with_attachment_that_fails_to_save
3612 set_tmp_attachments_directory
3630 set_tmp_attachments_directory
3613
3631
3614 # anonymous user
3632 # anonymous user
3615 with_settings :attachment_max_size => 0 do
3633 with_settings :attachment_max_size => 0 do
3616 put :update,
3634 put :update,
3617 :id => 1,
3635 :id => 1,
3618 :issue => {:notes => ''},
3636 :issue => {:notes => ''},
3619 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
3637 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
3620 assert_redirected_to :action => 'show', :id => '1'
3638 assert_redirected_to :action => 'show', :id => '1'
3621 assert_equal '1 file(s) could not be saved.', flash[:warning]
3639 assert_equal '1 file(s) could not be saved.', flash[:warning]
3622 end
3640 end
3623 end
3641 end
3624
3642
3625 def test_put_update_with_attachment_deletion_should_create_a_single_journal
3643 def test_put_update_with_attachment_deletion_should_create_a_single_journal
3626 set_tmp_attachments_directory
3644 set_tmp_attachments_directory
3627 @request.session[:user_id] = 2
3645 @request.session[:user_id] = 2
3628
3646
3629 journal = new_record(Journal) do
3647 journal = new_record(Journal) do
3630 assert_difference 'Attachment.count', -2 do
3648 assert_difference 'Attachment.count', -2 do
3631 put :update,
3649 put :update,
3632 :id => 3,
3650 :id => 3,
3633 :issue => {
3651 :issue => {
3634 :notes => 'Removing attachments',
3652 :notes => 'Removing attachments',
3635 :deleted_attachment_ids => ['1', '5']
3653 :deleted_attachment_ids => ['1', '5']
3636 }
3654 }
3637 end
3655 end
3638 end
3656 end
3639 assert_equal 'Removing attachments', journal.notes
3657 assert_equal 'Removing attachments', journal.notes
3640 assert_equal 2, journal.details.count
3658 assert_equal 2, journal.details.count
3641 end
3659 end
3642
3660
3643 def test_put_update_with_attachment_deletion_and_failure_should_preserve_selected_attachments
3661 def test_put_update_with_attachment_deletion_and_failure_should_preserve_selected_attachments
3644 set_tmp_attachments_directory
3662 set_tmp_attachments_directory
3645 @request.session[:user_id] = 2
3663 @request.session[:user_id] = 2
3646
3664
3647 assert_no_difference 'Journal.count' do
3665 assert_no_difference 'Journal.count' do
3648 assert_no_difference 'Attachment.count' do
3666 assert_no_difference 'Attachment.count' do
3649 put :update,
3667 put :update,
3650 :id => 3,
3668 :id => 3,
3651 :issue => {
3669 :issue => {
3652 :subject => '',
3670 :subject => '',
3653 :notes => 'Removing attachments',
3671 :notes => 'Removing attachments',
3654 :deleted_attachment_ids => ['1', '5']
3672 :deleted_attachment_ids => ['1', '5']
3655 }
3673 }
3656 end
3674 end
3657 end
3675 end
3658 assert_select 'input[name=?][value="1"][checked=checked]', 'issue[deleted_attachment_ids][]'
3676 assert_select 'input[name=?][value="1"][checked=checked]', 'issue[deleted_attachment_ids][]'
3659 assert_select 'input[name=?][value="5"][checked=checked]', 'issue[deleted_attachment_ids][]'
3677 assert_select 'input[name=?][value="5"][checked=checked]', 'issue[deleted_attachment_ids][]'
3660 assert_select 'input[name=?][value="6"]:not([checked])', 'issue[deleted_attachment_ids][]'
3678 assert_select 'input[name=?][value="6"]:not([checked])', 'issue[deleted_attachment_ids][]'
3661 end
3679 end
3662
3680
3663 def test_put_update_with_no_change
3681 def test_put_update_with_no_change
3664 issue = Issue.find(1)
3682 issue = Issue.find(1)
3665 issue.journals.clear
3683 issue.journals.clear
3666 ActionMailer::Base.deliveries.clear
3684 ActionMailer::Base.deliveries.clear
3667
3685
3668 put :update,
3686 put :update,
3669 :id => 1,
3687 :id => 1,
3670 :issue => {:notes => ''}
3688 :issue => {:notes => ''}
3671 assert_redirected_to :action => 'show', :id => '1'
3689 assert_redirected_to :action => 'show', :id => '1'
3672
3690
3673 issue.reload
3691 issue.reload
3674 assert issue.journals.empty?
3692 assert issue.journals.empty?
3675 # No email should be sent
3693 # No email should be sent
3676 assert ActionMailer::Base.deliveries.empty?
3694 assert ActionMailer::Base.deliveries.empty?
3677 end
3695 end
3678
3696
3679 def test_put_update_should_send_a_notification
3697 def test_put_update_should_send_a_notification
3680 @request.session[:user_id] = 2
3698 @request.session[:user_id] = 2
3681 ActionMailer::Base.deliveries.clear
3699 ActionMailer::Base.deliveries.clear
3682 issue = Issue.find(1)
3700 issue = Issue.find(1)
3683 old_subject = issue.subject
3701 old_subject = issue.subject
3684 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
3702 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
3685
3703
3686 with_settings :notified_events => %w(issue_updated) do
3704 with_settings :notified_events => %w(issue_updated) do
3687 put :update, :id => 1, :issue => {:subject => new_subject,
3705 put :update, :id => 1, :issue => {:subject => new_subject,
3688 :priority_id => '6',
3706 :priority_id => '6',
3689 :category_id => '1' # no change
3707 :category_id => '1' # no change
3690 }
3708 }
3691 assert_equal 1, ActionMailer::Base.deliveries.size
3709 assert_equal 1, ActionMailer::Base.deliveries.size
3692 end
3710 end
3693 end
3711 end
3694
3712
3695 def test_put_update_with_invalid_spent_time_hours_only
3713 def test_put_update_with_invalid_spent_time_hours_only
3696 @request.session[:user_id] = 2
3714 @request.session[:user_id] = 2
3697 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3715 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3698
3716
3699 assert_no_difference('Journal.count') do
3717 assert_no_difference('Journal.count') do
3700 put :update,
3718 put :update,
3701 :id => 1,
3719 :id => 1,
3702 :issue => {:notes => notes},
3720 :issue => {:notes => notes},
3703 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
3721 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
3704 end
3722 end
3705 assert_response :success
3723 assert_response :success
3706
3724
3707 assert_select_error /Activity cannot be blank/
3725 assert_select_error /Activity cannot be blank/
3708 assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
3726 assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
3709 assert_select 'input[name=?][value=?]', 'time_entry[hours]', '2z'
3727 assert_select 'input[name=?][value=?]', 'time_entry[hours]', '2z'
3710 end
3728 end
3711
3729
3712 def test_put_update_with_invalid_spent_time_comments_only
3730 def test_put_update_with_invalid_spent_time_comments_only
3713 @request.session[:user_id] = 2
3731 @request.session[:user_id] = 2
3714 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3732 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3715
3733
3716 assert_no_difference('Journal.count') do
3734 assert_no_difference('Journal.count') do
3717 put :update,
3735 put :update,
3718 :id => 1,
3736 :id => 1,
3719 :issue => {:notes => notes},
3737 :issue => {:notes => notes},
3720 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
3738 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
3721 end
3739 end
3722 assert_response :success
3740 assert_response :success
3723
3741
3724 assert_select_error /Activity cannot be blank/
3742 assert_select_error /Activity cannot be blank/
3725 assert_select_error /Hours cannot be blank/
3743 assert_select_error /Hours cannot be blank/
3726 assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
3744 assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
3727 assert_select 'input[name=?][value=?]', 'time_entry[comments]', 'this is my comment'
3745 assert_select 'input[name=?][value=?]', 'time_entry[comments]', 'this is my comment'
3728 end
3746 end
3729
3747
3730 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
3748 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
3731 issue = Issue.find(2)
3749 issue = Issue.find(2)
3732 @request.session[:user_id] = 2
3750 @request.session[:user_id] = 2
3733
3751
3734 put :update,
3752 put :update,
3735 :id => issue.id,
3753 :id => issue.id,
3736 :issue => {
3754 :issue => {
3737 :fixed_version_id => 4
3755 :fixed_version_id => 4
3738 }
3756 }
3739
3757
3740 assert_response :redirect
3758 assert_response :redirect
3741 issue.reload
3759 issue.reload
3742 assert_equal 4, issue.fixed_version_id
3760 assert_equal 4, issue.fixed_version_id
3743 assert_not_equal issue.project_id, issue.fixed_version.project_id
3761 assert_not_equal issue.project_id, issue.fixed_version.project_id
3744 end
3762 end
3745
3763
3746 def test_put_update_should_redirect_back_using_the_back_url_parameter
3764 def test_put_update_should_redirect_back_using_the_back_url_parameter
3747 issue = Issue.find(2)
3765 issue = Issue.find(2)
3748 @request.session[:user_id] = 2
3766 @request.session[:user_id] = 2
3749
3767
3750 put :update,
3768 put :update,
3751 :id => issue.id,
3769 :id => issue.id,
3752 :issue => {
3770 :issue => {
3753 :fixed_version_id => 4
3771 :fixed_version_id => 4
3754 },
3772 },
3755 :back_url => '/issues'
3773 :back_url => '/issues'
3756
3774
3757 assert_response :redirect
3775 assert_response :redirect
3758 assert_redirected_to '/issues'
3776 assert_redirected_to '/issues'
3759 end
3777 end
3760
3778
3761 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
3779 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
3762 issue = Issue.find(2)
3780 issue = Issue.find(2)
3763 @request.session[:user_id] = 2
3781 @request.session[:user_id] = 2
3764
3782
3765 put :update,
3783 put :update,
3766 :id => issue.id,
3784 :id => issue.id,
3767 :issue => {
3785 :issue => {
3768 :fixed_version_id => 4
3786 :fixed_version_id => 4
3769 },
3787 },
3770 :back_url => 'http://google.com'
3788 :back_url => 'http://google.com'
3771
3789
3772 assert_response :redirect
3790 assert_response :redirect
3773 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
3791 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
3774 end
3792 end
3775
3793
3776 def test_put_update_should_redirect_with_previous_and_next_issue_ids_params
3794 def test_put_update_should_redirect_with_previous_and_next_issue_ids_params
3777 @request.session[:user_id] = 2
3795 @request.session[:user_id] = 2
3778
3796
3779 put :update, :id => 11,
3797 put :update, :id => 11,
3780 :issue => {:status_id => 6, :notes => 'Notes'},
3798 :issue => {:status_id => 6, :notes => 'Notes'},
3781 :prev_issue_id => 8,
3799 :prev_issue_id => 8,
3782 :next_issue_id => 12,
3800 :next_issue_id => 12,
3783 :issue_position => 2,
3801 :issue_position => 2,
3784 :issue_count => 3
3802 :issue_count => 3
3785
3803
3786 assert_redirected_to '/issues/11?issue_count=3&issue_position=2&next_issue_id=12&prev_issue_id=8'
3804 assert_redirected_to '/issues/11?issue_count=3&issue_position=2&next_issue_id=12&prev_issue_id=8'
3787 end
3805 end
3788
3806
3789 def test_update_with_permission_on_tracker_should_be_allowed
3807 def test_update_with_permission_on_tracker_should_be_allowed
3790 role = Role.find(1)
3808 role = Role.find(1)
3791 role.set_permission_trackers :edit_issues, [1]
3809 role.set_permission_trackers :edit_issues, [1]
3792 role.save!
3810 role.save!
3793 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Original subject')
3811 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Original subject')
3794
3812
3795 @request.session[:user_id] = 2
3813 @request.session[:user_id] = 2
3796 put :update, :id => issue.id, :issue => {:subject => 'Changed subject'}
3814 put :update, :id => issue.id, :issue => {:subject => 'Changed subject'}
3797 assert_response 302
3815 assert_response 302
3798 assert_equal 'Changed subject', issue.reload.subject
3816 assert_equal 'Changed subject', issue.reload.subject
3799 end
3817 end
3800
3818
3801 def test_update_without_permission_on_tracker_should_be_denied
3819 def test_update_without_permission_on_tracker_should_be_denied
3802 role = Role.find(1)
3820 role = Role.find(1)
3803 role.set_permission_trackers :edit_issues, [1]
3821 role.set_permission_trackers :edit_issues, [1]
3804 role.save!
3822 role.save!
3805 issue = Issue.generate!(:project_id => 1, :tracker_id => 2, :subject => 'Original subject')
3823 issue = Issue.generate!(:project_id => 1, :tracker_id => 2, :subject => 'Original subject')
3806
3824
3807 @request.session[:user_id] = 2
3825 @request.session[:user_id] = 2
3808 put :update, :id => issue.id, :issue => {:subject => 'Changed subject'}
3826 put :update, :id => issue.id, :issue => {:subject => 'Changed subject'}
3809 assert_response 302
3827 assert_response 302
3810 assert_equal 'Original subject', issue.reload.subject
3828 assert_equal 'Original subject', issue.reload.subject
3811 end
3829 end
3812
3830
3813 def test_get_bulk_edit
3831 def test_get_bulk_edit
3814 @request.session[:user_id] = 2
3832 @request.session[:user_id] = 2
3815 get :bulk_edit, :ids => [1, 3]
3833 get :bulk_edit, :ids => [1, 3]
3816 assert_response :success
3834 assert_response :success
3817
3835
3818 assert_select 'ul#bulk-selection' do
3836 assert_select 'ul#bulk-selection' do
3819 assert_select 'li', 2
3837 assert_select 'li', 2
3820 assert_select 'li a', :text => 'Bug #1'
3838 assert_select 'li a', :text => 'Bug #1'
3821 end
3839 end
3822
3840
3823 assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
3841 assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
3824 assert_select 'input[name=?]', 'ids[]', 2
3842 assert_select 'input[name=?]', 'ids[]', 2
3825 assert_select 'input[name=?][value="1"][type=hidden]', 'ids[]'
3843 assert_select 'input[name=?][value="1"][type=hidden]', 'ids[]'
3826
3844
3827 assert_select 'select[name=?]', 'issue[project_id]'
3845 assert_select 'select[name=?]', 'issue[project_id]'
3828 assert_select 'input[name=?]', 'issue[parent_issue_id]'
3846 assert_select 'input[name=?]', 'issue[parent_issue_id]'
3829
3847
3830 # Project specific custom field, date type
3848 # Project specific custom field, date type
3831 field = CustomField.find(9)
3849 field = CustomField.find(9)
3832 assert !field.is_for_all?
3850 assert !field.is_for_all?
3833 assert_equal 'date', field.field_format
3851 assert_equal 'date', field.field_format
3834 assert_select 'input[name=?]', 'issue[custom_field_values][9]'
3852 assert_select 'input[name=?]', 'issue[custom_field_values][9]'
3835
3853
3836 # System wide custom field
3854 # System wide custom field
3837 assert CustomField.find(1).is_for_all?
3855 assert CustomField.find(1).is_for_all?
3838 assert_select 'select[name=?]', 'issue[custom_field_values][1]'
3856 assert_select 'select[name=?]', 'issue[custom_field_values][1]'
3839
3857
3840 # Be sure we don't display inactive IssuePriorities
3858 # Be sure we don't display inactive IssuePriorities
3841 assert ! IssuePriority.find(15).active?
3859 assert ! IssuePriority.find(15).active?
3842 assert_select 'select[name=?]', 'issue[priority_id]' do
3860 assert_select 'select[name=?]', 'issue[priority_id]' do
3843 assert_select 'option[value="15"]', 0
3861 assert_select 'option[value="15"]', 0
3844 end
3862 end
3845 end
3863 end
3846 end
3864 end
3847
3865
3848 def test_get_bulk_edit_on_different_projects
3866 def test_get_bulk_edit_on_different_projects
3849 @request.session[:user_id] = 2
3867 @request.session[:user_id] = 2
3850 get :bulk_edit, :ids => [1, 2, 6]
3868 get :bulk_edit, :ids => [1, 2, 6]
3851 assert_response :success
3869 assert_response :success
3852
3870
3853 # Can not set issues from different projects as children of an issue
3871 # Can not set issues from different projects as children of an issue
3854 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
3872 assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
3855
3873
3856 # Project specific custom field, date type
3874 # Project specific custom field, date type
3857 field = CustomField.find(9)
3875 field = CustomField.find(9)
3858 assert !field.is_for_all?
3876 assert !field.is_for_all?
3859 assert !field.project_ids.include?(Issue.find(6).project_id)
3877 assert !field.project_ids.include?(Issue.find(6).project_id)
3860 assert_select 'input[name=?]', 'issue[custom_field_values][9]', 0
3878 assert_select 'input[name=?]', 'issue[custom_field_values][9]', 0
3861 end
3879 end
3862
3880
3863 def test_get_bulk_edit_with_user_custom_field
3881 def test_get_bulk_edit_with_user_custom_field
3864 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true, :tracker_ids => [1,2,3])
3882 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true, :tracker_ids => [1,2,3])
3865
3883
3866 @request.session[:user_id] = 2
3884 @request.session[:user_id] = 2
3867 get :bulk_edit, :ids => [1, 2]
3885 get :bulk_edit, :ids => [1, 2]
3868 assert_response :success
3886 assert_response :success
3869
3887
3870 assert_select 'select.user_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
3888 assert_select 'select.user_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
3871 assert_select 'option', Project.find(1).users.count + 2 # "no change" + "none" options
3889 assert_select 'option', Project.find(1).users.count + 2 # "no change" + "none" options
3872 end
3890 end
3873 end
3891 end
3874
3892
3875 def test_get_bulk_edit_with_version_custom_field
3893 def test_get_bulk_edit_with_version_custom_field
3876 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
3894 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
3877
3895
3878 @request.session[:user_id] = 2
3896 @request.session[:user_id] = 2
3879 get :bulk_edit, :ids => [1, 2]
3897 get :bulk_edit, :ids => [1, 2]
3880 assert_response :success
3898 assert_response :success
3881
3899
3882 assert_select 'select.version_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
3900 assert_select 'select.version_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
3883 assert_select 'option', Project.find(1).shared_versions.count + 2 # "no change" + "none" options
3901 assert_select 'option', Project.find(1).shared_versions.count + 2 # "no change" + "none" options
3884 end
3902 end
3885 end
3903 end
3886
3904
3887 def test_get_bulk_edit_with_multi_custom_field
3905 def test_get_bulk_edit_with_multi_custom_field
3888 field = CustomField.find(1)
3906 field = CustomField.find(1)
3889 field.update_attribute :multiple, true
3907 field.update_attribute :multiple, true
3890
3908
3891 @request.session[:user_id] = 2
3909 @request.session[:user_id] = 2
3892 get :bulk_edit, :ids => [1, 3]
3910 get :bulk_edit, :ids => [1, 3]
3893 assert_response :success
3911 assert_response :success
3894
3912
3895 assert_select 'select[name=?]', 'issue[custom_field_values][1][]' do
3913 assert_select 'select[name=?]', 'issue[custom_field_values][1][]' do
3896 assert_select 'option', field.possible_values.size + 1 # "none" options
3914 assert_select 'option', field.possible_values.size + 1 # "none" options
3897 end
3915 end
3898 end
3916 end
3899
3917
3900 def test_bulk_edit_should_propose_to_clear_text_custom_fields
3918 def test_bulk_edit_should_propose_to_clear_text_custom_fields
3901 @request.session[:user_id] = 2
3919 @request.session[:user_id] = 2
3902 get :bulk_edit, :ids => [1, 3]
3920 get :bulk_edit, :ids => [1, 3]
3903 assert_response :success
3921 assert_response :success
3904
3922
3905 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', '__none__'
3923 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', '__none__'
3906 end
3924 end
3907
3925
3908 def test_bulk_edit_should_only_propose_statuses_allowed_for_all_issues
3926 def test_bulk_edit_should_only_propose_statuses_allowed_for_all_issues
3909 WorkflowTransition.delete_all
3927 WorkflowTransition.delete_all
3910 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3928 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3911 :old_status_id => 1, :new_status_id => 1)
3929 :old_status_id => 1, :new_status_id => 1)
3912 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3930 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3913 :old_status_id => 1, :new_status_id => 3)
3931 :old_status_id => 1, :new_status_id => 3)
3914 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3932 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
3915 :old_status_id => 1, :new_status_id => 4)
3933 :old_status_id => 1, :new_status_id => 4)
3916 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3934 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3917 :old_status_id => 2, :new_status_id => 1)
3935 :old_status_id => 2, :new_status_id => 1)
3918 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3936 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3919 :old_status_id => 2, :new_status_id => 3)
3937 :old_status_id => 2, :new_status_id => 3)
3920 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3938 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
3921 :old_status_id => 2, :new_status_id => 5)
3939 :old_status_id => 2, :new_status_id => 5)
3922 @request.session[:user_id] = 2
3940 @request.session[:user_id] = 2
3923 get :bulk_edit, :ids => [1, 2]
3941 get :bulk_edit, :ids => [1, 2]
3924
3942
3925 assert_select 'select[name=?]', 'issue[status_id]' do
3943 assert_select 'select[name=?]', 'issue[status_id]' do
3926 assert_select 'option[value=""]'
3944 assert_select 'option[value=""]'
3927 assert_select 'option[value="1"]'
3945 assert_select 'option[value="1"]'
3928 assert_select 'option[value="3"]'
3946 assert_select 'option[value="3"]'
3929 assert_select 'option', 3 # 2 statuses + "no change" option
3947 assert_select 'option', 3 # 2 statuses + "no change" option
3930 end
3948 end
3931 end
3949 end
3932
3950
3933 def test_bulk_edit_should_propose_target_project_open_shared_versions
3951 def test_bulk_edit_should_propose_target_project_open_shared_versions
3934 @request.session[:user_id] = 2
3952 @request.session[:user_id] = 2
3935 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3953 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3936 assert_response :success
3954 assert_response :success
3937
3955
3938 expected_versions = Project.find(1).shared_versions.open.to_a.sort
3956 expected_versions = Project.find(1).shared_versions.open.to_a.sort
3939
3957
3940 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
3958 assert_select 'select[name=?]', 'issue[fixed_version_id]' do
3941 expected_versions.each do |version|
3959 expected_versions.each do |version|
3942 assert_select 'option[value=?]', version.id.to_s
3960 assert_select 'option[value=?]', version.id.to_s
3943 end
3961 end
3944 assert_select 'option[value=""]'
3962 assert_select 'option[value=""]'
3945 assert_select 'option[value="none"]'
3963 assert_select 'option[value="none"]'
3946 assert_select 'option', expected_versions.size + 2
3964 assert_select 'option', expected_versions.size + 2
3947 end
3965 end
3948 end
3966 end
3949
3967
3950 def test_bulk_edit_should_propose_target_project_categories
3968 def test_bulk_edit_should_propose_target_project_categories
3951 @request.session[:user_id] = 2
3969 @request.session[:user_id] = 2
3952 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3970 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3953 assert_response :success
3971 assert_response :success
3954
3972
3955 expected_categories = Project.find(1).issue_categories.sort
3973 expected_categories = Project.find(1).issue_categories.sort
3956
3974
3957 assert_select 'select[name=?]', 'issue[category_id]' do
3975 assert_select 'select[name=?]', 'issue[category_id]' do
3958 expected_categories.each do |category|
3976 expected_categories.each do |category|
3959 assert_select 'option[value=?]', category.id.to_s
3977 assert_select 'option[value=?]', category.id.to_s
3960 end
3978 end
3961 assert_select 'option[value=""]'
3979 assert_select 'option[value=""]'
3962 assert_select 'option[value="none"]'
3980 assert_select 'option[value="none"]'
3963 assert_select 'option', expected_categories.size + 2
3981 assert_select 'option', expected_categories.size + 2
3964 end
3982 end
3965 end
3983 end
3966
3984
3967 def test_bulk_edit_should_only_propose_issues_trackers_custom_fields
3985 def test_bulk_edit_should_only_propose_issues_trackers_custom_fields
3968 IssueCustomField.delete_all
3986 IssueCustomField.delete_all
3969 field1 = IssueCustomField.generate!(:tracker_ids => [1], :is_for_all => true)
3987 field1 = IssueCustomField.generate!(:tracker_ids => [1], :is_for_all => true)
3970 field2 = IssueCustomField.generate!(:tracker_ids => [2], :is_for_all => true)
3988 field2 = IssueCustomField.generate!(:tracker_ids => [2], :is_for_all => true)
3971 @request.session[:user_id] = 2
3989 @request.session[:user_id] = 2
3972
3990
3973 issue_ids = Issue.where(:project_id => 1, :tracker_id => 1).limit(2).ids
3991 issue_ids = Issue.where(:project_id => 1, :tracker_id => 1).limit(2).ids
3974 get :bulk_edit, :ids => issue_ids
3992 get :bulk_edit, :ids => issue_ids
3975 assert_response :success
3993 assert_response :success
3976
3994
3977 assert_select 'input[name=?]', "issue[custom_field_values][#{field1.id}]"
3995 assert_select 'input[name=?]', "issue[custom_field_values][#{field1.id}]"
3978 assert_select 'input[name=?]', "issue[custom_field_values][#{field2.id}]", 0
3996 assert_select 'input[name=?]', "issue[custom_field_values][#{field2.id}]", 0
3979 end
3997 end
3980
3998
3981 def test_bulk_update
3999 def test_bulk_update
3982 @request.session[:user_id] = 2
4000 @request.session[:user_id] = 2
3983 # update issues priority
4001 # update issues priority
3984 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
4002 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
3985 :issue => {:priority_id => 7,
4003 :issue => {:priority_id => 7,
3986 :assigned_to_id => '',
4004 :assigned_to_id => '',
3987 :custom_field_values => {'2' => ''}}
4005 :custom_field_values => {'2' => ''}}
3988
4006
3989 assert_response 302
4007 assert_response 302
3990 # check that the issues were updated
4008 # check that the issues were updated
3991 assert_equal [7, 7], Issue.where(:id =>[1, 2]).collect {|i| i.priority.id}
4009 assert_equal [7, 7], Issue.where(:id =>[1, 2]).collect {|i| i.priority.id}
3992
4010
3993 issue = Issue.find(1)
4011 issue = Issue.find(1)
3994 journal = issue.journals.reorder('created_on DESC').first
4012 journal = issue.journals.reorder('created_on DESC').first
3995 assert_equal '125', issue.custom_value_for(2).value
4013 assert_equal '125', issue.custom_value_for(2).value
3996 assert_equal 'Bulk editing', journal.notes
4014 assert_equal 'Bulk editing', journal.notes
3997 assert_equal 1, journal.details.size
4015 assert_equal 1, journal.details.size
3998 end
4016 end
3999
4017
4000 def test_bulk_update_with_group_assignee
4018 def test_bulk_update_with_group_assignee
4001 group = Group.find(11)
4019 group = Group.find(11)
4002 project = Project.find(1)
4020 project = Project.find(1)
4003 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
4021 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
4004
4022
4005 @request.session[:user_id] = 2
4023 @request.session[:user_id] = 2
4006 # update issues assignee
4024 # update issues assignee
4007 with_settings :issue_group_assignment => '1' do
4025 with_settings :issue_group_assignment => '1' do
4008 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
4026 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
4009 :issue => {:priority_id => '',
4027 :issue => {:priority_id => '',
4010 :assigned_to_id => group.id,
4028 :assigned_to_id => group.id,
4011 :custom_field_values => {'2' => ''}}
4029 :custom_field_values => {'2' => ''}}
4012
4030
4013 assert_response 302
4031 assert_response 302
4014 assert_equal [group, group], Issue.where(:id => [1, 2]).collect {|i| i.assigned_to}
4032 assert_equal [group, group], Issue.where(:id => [1, 2]).collect {|i| i.assigned_to}
4015 end
4033 end
4016 end
4034 end
4017
4035
4018 def test_bulk_update_on_different_projects
4036 def test_bulk_update_on_different_projects
4019 @request.session[:user_id] = 2
4037 @request.session[:user_id] = 2
4020 # update issues priority
4038 # update issues priority
4021 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
4039 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
4022 :issue => {:priority_id => 7,
4040 :issue => {:priority_id => 7,
4023 :assigned_to_id => '',
4041 :assigned_to_id => '',
4024 :custom_field_values => {'2' => ''}}
4042 :custom_field_values => {'2' => ''}}
4025
4043
4026 assert_response 302
4044 assert_response 302
4027 # check that the issues were updated
4045 # check that the issues were updated
4028 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
4046 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
4029
4047
4030 issue = Issue.find(1)
4048 issue = Issue.find(1)
4031 journal = issue.journals.reorder('created_on DESC').first
4049 journal = issue.journals.reorder('created_on DESC').first
4032 assert_equal '125', issue.custom_value_for(2).value
4050 assert_equal '125', issue.custom_value_for(2).value
4033 assert_equal 'Bulk editing', journal.notes
4051 assert_equal 'Bulk editing', journal.notes
4034 assert_equal 1, journal.details.size
4052 assert_equal 1, journal.details.size
4035 end
4053 end
4036
4054
4037 def test_bulk_update_on_different_projects_without_rights
4055 def test_bulk_update_on_different_projects_without_rights
4038 @request.session[:user_id] = 3
4056 @request.session[:user_id] = 3
4039 user = User.find(3)
4057 user = User.find(3)
4040 action = { :controller => "issues", :action => "bulk_update" }
4058 action = { :controller => "issues", :action => "bulk_update" }
4041 assert user.allowed_to?(action, Issue.find(1).project)
4059 assert user.allowed_to?(action, Issue.find(1).project)
4042 assert ! user.allowed_to?(action, Issue.find(6).project)
4060 assert ! user.allowed_to?(action, Issue.find(6).project)
4043 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
4061 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
4044 :issue => {:priority_id => 7,
4062 :issue => {:priority_id => 7,
4045 :assigned_to_id => '',
4063 :assigned_to_id => '',
4046 :custom_field_values => {'2' => ''}}
4064 :custom_field_values => {'2' => ''}}
4047 assert_response 403
4065 assert_response 403
4048 assert_not_equal "Bulk should fail", Journal.last.notes
4066 assert_not_equal "Bulk should fail", Journal.last.notes
4049 end
4067 end
4050
4068
4051 def test_bullk_update_should_send_a_notification
4069 def test_bullk_update_should_send_a_notification
4052 @request.session[:user_id] = 2
4070 @request.session[:user_id] = 2
4053 ActionMailer::Base.deliveries.clear
4071 ActionMailer::Base.deliveries.clear
4054 with_settings :notified_events => %w(issue_updated) do
4072 with_settings :notified_events => %w(issue_updated) do
4055 post(:bulk_update,
4073 post(:bulk_update,
4056 {
4074 {
4057 :ids => [1, 2],
4075 :ids => [1, 2],
4058 :notes => 'Bulk editing',
4076 :notes => 'Bulk editing',
4059 :issue => {
4077 :issue => {
4060 :priority_id => 7,
4078 :priority_id => 7,
4061 :assigned_to_id => '',
4079 :assigned_to_id => '',
4062 :custom_field_values => {'2' => ''}
4080 :custom_field_values => {'2' => ''}
4063 }
4081 }
4064 })
4082 })
4065 assert_response 302
4083 assert_response 302
4066 assert_equal 2, ActionMailer::Base.deliveries.size
4084 assert_equal 2, ActionMailer::Base.deliveries.size
4067 end
4085 end
4068 end
4086 end
4069
4087
4070 def test_bulk_update_project
4088 def test_bulk_update_project
4071 @request.session[:user_id] = 2
4089 @request.session[:user_id] = 2
4072 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
4090 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
4073 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4091 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4074 # Issues moved to project 2
4092 # Issues moved to project 2
4075 assert_equal 2, Issue.find(1).project_id
4093 assert_equal 2, Issue.find(1).project_id
4076 assert_equal 2, Issue.find(2).project_id
4094 assert_equal 2, Issue.find(2).project_id
4077 # No tracker change
4095 # No tracker change
4078 assert_equal 1, Issue.find(1).tracker_id
4096 assert_equal 1, Issue.find(1).tracker_id
4079 assert_equal 2, Issue.find(2).tracker_id
4097 assert_equal 2, Issue.find(2).tracker_id
4080 end
4098 end
4081
4099
4082 def test_bulk_update_project_on_single_issue_should_follow_when_needed
4100 def test_bulk_update_project_on_single_issue_should_follow_when_needed
4083 @request.session[:user_id] = 2
4101 @request.session[:user_id] = 2
4084 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
4102 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
4085 assert_redirected_to '/issues/1'
4103 assert_redirected_to '/issues/1'
4086 end
4104 end
4087
4105
4088 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
4106 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
4089 @request.session[:user_id] = 2
4107 @request.session[:user_id] = 2
4090 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
4108 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
4091 assert_redirected_to '/projects/onlinestore/issues'
4109 assert_redirected_to '/projects/onlinestore/issues'
4092 end
4110 end
4093
4111
4094 def test_bulk_update_tracker
4112 def test_bulk_update_tracker
4095 @request.session[:user_id] = 2
4113 @request.session[:user_id] = 2
4096 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
4114 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
4097 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4115 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4098 assert_equal 2, Issue.find(1).tracker_id
4116 assert_equal 2, Issue.find(1).tracker_id
4099 assert_equal 2, Issue.find(2).tracker_id
4117 assert_equal 2, Issue.find(2).tracker_id
4100 end
4118 end
4101
4119
4102 def test_bulk_update_status
4120 def test_bulk_update_status
4103 @request.session[:user_id] = 2
4121 @request.session[:user_id] = 2
4104 # update issues priority
4122 # update issues priority
4105 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
4123 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
4106 :issue => {:priority_id => '',
4124 :issue => {:priority_id => '',
4107 :assigned_to_id => '',
4125 :assigned_to_id => '',
4108 :status_id => '5'}
4126 :status_id => '5'}
4109
4127
4110 assert_response 302
4128 assert_response 302
4111 issue = Issue.find(1)
4129 issue = Issue.find(1)
4112 assert issue.closed?
4130 assert issue.closed?
4113 end
4131 end
4114
4132
4115 def test_bulk_update_priority
4133 def test_bulk_update_priority
4116 @request.session[:user_id] = 2
4134 @request.session[:user_id] = 2
4117 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
4135 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
4118
4136
4119 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4137 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4120 assert_equal 6, Issue.find(1).priority_id
4138 assert_equal 6, Issue.find(1).priority_id
4121 assert_equal 6, Issue.find(2).priority_id
4139 assert_equal 6, Issue.find(2).priority_id
4122 end
4140 end
4123
4141
4124 def test_bulk_update_with_notes
4142 def test_bulk_update_with_notes
4125 @request.session[:user_id] = 2
4143 @request.session[:user_id] = 2
4126 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
4144 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
4127
4145
4128 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4146 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4129 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
4147 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
4130 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
4148 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
4131 end
4149 end
4132
4150
4133 def test_bulk_update_parent_id
4151 def test_bulk_update_parent_id
4134 IssueRelation.delete_all
4152 IssueRelation.delete_all
4135 @request.session[:user_id] = 2
4153 @request.session[:user_id] = 2
4136 post :bulk_update, :ids => [1, 3],
4154 post :bulk_update, :ids => [1, 3],
4137 :notes => 'Bulk editing parent',
4155 :notes => 'Bulk editing parent',
4138 :issue => {:priority_id => '', :assigned_to_id => '',
4156 :issue => {:priority_id => '', :assigned_to_id => '',
4139 :status_id => '', :parent_issue_id => '2'}
4157 :status_id => '', :parent_issue_id => '2'}
4140 assert_response 302
4158 assert_response 302
4141 parent = Issue.find(2)
4159 parent = Issue.find(2)
4142 assert_equal parent.id, Issue.find(1).parent_id
4160 assert_equal parent.id, Issue.find(1).parent_id
4143 assert_equal parent.id, Issue.find(3).parent_id
4161 assert_equal parent.id, Issue.find(3).parent_id
4144 assert_equal [1, 3], parent.children.collect(&:id).sort
4162 assert_equal [1, 3], parent.children.collect(&:id).sort
4145 end
4163 end
4146
4164
4147 def test_bulk_update_estimated_hours
4165 def test_bulk_update_estimated_hours
4148 @request.session[:user_id] = 2
4166 @request.session[:user_id] = 2
4149 post :bulk_update, :ids => [1, 2], :issue => {:estimated_hours => 4.25}
4167 post :bulk_update, :ids => [1, 2], :issue => {:estimated_hours => 4.25}
4150
4168
4151 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4169 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
4152 assert_equal 4.25, Issue.find(1).estimated_hours
4170 assert_equal 4.25, Issue.find(1).estimated_hours
4153 assert_equal 4.25, Issue.find(2).estimated_hours
4171 assert_equal 4.25, Issue.find(2).estimated_hours
4154 end
4172 end
4155
4173
4156 def test_bulk_update_custom_field
4174 def test_bulk_update_custom_field
4157 @request.session[:user_id] = 2
4175 @request.session[:user_id] = 2
4158 # update issues priority
4176 # update issues priority
4159 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
4177 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
4160 :issue => {:priority_id => '',
4178 :issue => {:priority_id => '',
4161 :assigned_to_id => '',
4179 :assigned_to_id => '',
4162 :custom_field_values => {'2' => '777'}}
4180 :custom_field_values => {'2' => '777'}}
4163
4181
4164 assert_response 302
4182 assert_response 302
4165
4183
4166 issue = Issue.find(1)
4184 issue = Issue.find(1)
4167 journal = issue.journals.reorder('created_on DESC').first
4185 journal = issue.journals.reorder('created_on DESC').first
4168 assert_equal '777', issue.custom_value_for(2).value
4186 assert_equal '777', issue.custom_value_for(2).value
4169 assert_equal 1, journal.details.size
4187 assert_equal 1, journal.details.size
4170 assert_equal '125', journal.details.first.old_value
4188 assert_equal '125', journal.details.first.old_value
4171 assert_equal '777', journal.details.first.value
4189 assert_equal '777', journal.details.first.value
4172 end
4190 end
4173
4191
4174 def test_bulk_update_custom_field_to_blank
4192 def test_bulk_update_custom_field_to_blank
4175 @request.session[:user_id] = 2
4193 @request.session[:user_id] = 2
4176 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing custom field',
4194 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing custom field',
4177 :issue => {:priority_id => '',
4195 :issue => {:priority_id => '',
4178 :assigned_to_id => '',
4196 :assigned_to_id => '',
4179 :custom_field_values => {'1' => '__none__'}}
4197 :custom_field_values => {'1' => '__none__'}}
4180 assert_response 302
4198 assert_response 302
4181 assert_equal '', Issue.find(1).custom_field_value(1)
4199 assert_equal '', Issue.find(1).custom_field_value(1)
4182 assert_equal '', Issue.find(3).custom_field_value(1)
4200 assert_equal '', Issue.find(3).custom_field_value(1)
4183 end
4201 end
4184
4202
4185 def test_bulk_update_multi_custom_field
4203 def test_bulk_update_multi_custom_field
4186 field = CustomField.find(1)
4204 field = CustomField.find(1)
4187 field.update_attribute :multiple, true
4205 field.update_attribute :multiple, true
4188
4206
4189 @request.session[:user_id] = 2
4207 @request.session[:user_id] = 2
4190 post :bulk_update, :ids => [1, 2, 3], :notes => 'Bulk editing multi custom field',
4208 post :bulk_update, :ids => [1, 2, 3], :notes => 'Bulk editing multi custom field',
4191 :issue => {:priority_id => '',
4209 :issue => {:priority_id => '',
4192 :assigned_to_id => '',
4210 :assigned_to_id => '',
4193 :custom_field_values => {'1' => ['MySQL', 'Oracle']}}
4211 :custom_field_values => {'1' => ['MySQL', 'Oracle']}}
4194
4212
4195 assert_response 302
4213 assert_response 302
4196
4214
4197 assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
4215 assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
4198 assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
4216 assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
4199 # the custom field is not associated with the issue tracker
4217 # the custom field is not associated with the issue tracker
4200 assert_nil Issue.find(2).custom_field_value(1)
4218 assert_nil Issue.find(2).custom_field_value(1)
4201 end
4219 end
4202
4220
4203 def test_bulk_update_multi_custom_field_to_blank
4221 def test_bulk_update_multi_custom_field_to_blank
4204 field = CustomField.find(1)
4222 field = CustomField.find(1)
4205 field.update_attribute :multiple, true
4223 field.update_attribute :multiple, true
4206
4224
4207 @request.session[:user_id] = 2
4225 @request.session[:user_id] = 2
4208 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing multi custom field',
4226 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing multi custom field',
4209 :issue => {:priority_id => '',
4227 :issue => {:priority_id => '',
4210 :assigned_to_id => '',
4228 :assigned_to_id => '',
4211 :custom_field_values => {'1' => ['__none__']}}
4229 :custom_field_values => {'1' => ['__none__']}}
4212 assert_response 302
4230 assert_response 302
4213 assert_equal [''], Issue.find(1).custom_field_value(1)
4231 assert_equal [''], Issue.find(1).custom_field_value(1)
4214 assert_equal [''], Issue.find(3).custom_field_value(1)
4232 assert_equal [''], Issue.find(3).custom_field_value(1)
4215 end
4233 end
4216
4234
4217 def test_bulk_update_unassign
4235 def test_bulk_update_unassign
4218 assert_not_nil Issue.find(2).assigned_to
4236 assert_not_nil Issue.find(2).assigned_to
4219 @request.session[:user_id] = 2
4237 @request.session[:user_id] = 2
4220 # unassign issues
4238 # unassign issues
4221 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
4239 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
4222 assert_response 302
4240 assert_response 302
4223 # check that the issues were updated
4241 # check that the issues were updated
4224 assert_nil Issue.find(2).assigned_to
4242 assert_nil Issue.find(2).assigned_to
4225 end
4243 end
4226
4244
4227 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
4245 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
4228 @request.session[:user_id] = 2
4246 @request.session[:user_id] = 2
4229
4247
4230 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
4248 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
4231
4249
4232 assert_response :redirect
4250 assert_response :redirect
4233 issues = Issue.find([1,2])
4251 issues = Issue.find([1,2])
4234 issues.each do |issue|
4252 issues.each do |issue|
4235 assert_equal 4, issue.fixed_version_id
4253 assert_equal 4, issue.fixed_version_id
4236 assert_not_equal issue.project_id, issue.fixed_version.project_id
4254 assert_not_equal issue.project_id, issue.fixed_version.project_id
4237 end
4255 end
4238 end
4256 end
4239
4257
4240 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
4258 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
4241 @request.session[:user_id] = 2
4259 @request.session[:user_id] = 2
4242 post :bulk_update, :ids => [1,2], :back_url => '/issues'
4260 post :bulk_update, :ids => [1,2], :back_url => '/issues'
4243
4261
4244 assert_response :redirect
4262 assert_response :redirect
4245 assert_redirected_to '/issues'
4263 assert_redirected_to '/issues'
4246 end
4264 end
4247
4265
4248 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
4266 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
4249 @request.session[:user_id] = 2
4267 @request.session[:user_id] = 2
4250 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
4268 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
4251
4269
4252 assert_response :redirect
4270 assert_response :redirect
4253 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
4271 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
4254 end
4272 end
4255
4273
4256 def test_bulk_update_with_all_failures_should_show_errors
4274 def test_bulk_update_with_all_failures_should_show_errors
4257 @request.session[:user_id] = 2
4275 @request.session[:user_id] = 2
4258 post :bulk_update, :ids => [1, 2], :issue => {:start_date => 'foo'}
4276 post :bulk_update, :ids => [1, 2], :issue => {:start_date => 'foo'}
4259 assert_response :success
4277 assert_response :success
4260
4278
4261 assert_select '#errorExplanation span', :text => 'Failed to save 2 issue(s) on 2 selected: #1, #2.'
4279 assert_select '#errorExplanation span', :text => 'Failed to save 2 issue(s) on 2 selected: #1, #2.'
4262 assert_select '#errorExplanation ul li', :text => 'Start date is not a valid date: #1, #2'
4280 assert_select '#errorExplanation ul li', :text => 'Start date is not a valid date: #1, #2'
4263 end
4281 end
4264
4282
4265 def test_bulk_update_with_some_failures_should_show_errors
4283 def test_bulk_update_with_some_failures_should_show_errors
4266 issue1 = Issue.generate!(:start_date => '2013-05-12')
4284 issue1 = Issue.generate!(:start_date => '2013-05-12')
4267 issue2 = Issue.generate!(:start_date => '2013-05-15')
4285 issue2 = Issue.generate!(:start_date => '2013-05-15')
4268 issue3 = Issue.generate!
4286 issue3 = Issue.generate!
4269 @request.session[:user_id] = 2
4287 @request.session[:user_id] = 2
4270 post :bulk_update, :ids => [issue1.id, issue2.id, issue3.id],
4288 post :bulk_update, :ids => [issue1.id, issue2.id, issue3.id],
4271 :issue => {:due_date => '2013-05-01'}
4289 :issue => {:due_date => '2013-05-01'}
4272 assert_response :success
4290 assert_response :success
4273
4291
4274 assert_select '#errorExplanation span',
4292 assert_select '#errorExplanation span',
4275 :text => "Failed to save 2 issue(s) on 3 selected: ##{issue1.id}, ##{issue2.id}."
4293 :text => "Failed to save 2 issue(s) on 3 selected: ##{issue1.id}, ##{issue2.id}."
4276 assert_select '#errorExplanation ul li',
4294 assert_select '#errorExplanation ul li',
4277 :text => "Due date must be greater than start date: ##{issue1.id}, ##{issue2.id}"
4295 :text => "Due date must be greater than start date: ##{issue1.id}, ##{issue2.id}"
4278
4296
4279 assert_select '#bulk-selection li', 2
4297 assert_select '#bulk-selection li', 2
4280 end
4298 end
4281
4299
4282 def test_bulk_update_with_failure_should_preserved_form_values
4300 def test_bulk_update_with_failure_should_preserved_form_values
4283 @request.session[:user_id] = 2
4301 @request.session[:user_id] = 2
4284 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2', :start_date => 'foo'}
4302 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2', :start_date => 'foo'}
4285 assert_response :success
4303 assert_response :success
4286
4304
4287 assert_select 'select[name=?]', 'issue[tracker_id]' do
4305 assert_select 'select[name=?]', 'issue[tracker_id]' do
4288 assert_select 'option[value="2"][selected=selected]'
4306 assert_select 'option[value="2"][selected=selected]'
4289 end
4307 end
4290 assert_select 'input[name=?][value=?]', 'issue[start_date]', 'foo'
4308 assert_select 'input[name=?][value=?]', 'issue[start_date]', 'foo'
4291 end
4309 end
4292
4310
4293 def test_get_bulk_copy
4311 def test_get_bulk_copy
4294 @request.session[:user_id] = 2
4312 @request.session[:user_id] = 2
4295 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
4313 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
4296 assert_response :success
4314 assert_response :success
4297
4315
4298 assert_select '#bulk-selection li', 3
4316 assert_select '#bulk-selection li', 3
4299
4317
4300 assert_select 'select[name=?]', 'issue[project_id]' do
4318 assert_select 'select[name=?]', 'issue[project_id]' do
4301 assert_select 'option[value=""]'
4319 assert_select 'option[value=""]'
4302 end
4320 end
4303 assert_select 'input[name=copy_attachments]'
4321 assert_select 'input[name=copy_attachments]'
4304 end
4322 end
4305
4323
4306 def test_get_bulk_copy_without_add_issues_permission_should_not_propose_current_project_as_target
4324 def test_get_bulk_copy_without_add_issues_permission_should_not_propose_current_project_as_target
4307 user = setup_user_with_copy_but_not_add_permission
4325 user = setup_user_with_copy_but_not_add_permission
4308 @request.session[:user_id] = user.id
4326 @request.session[:user_id] = user.id
4309
4327
4310 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
4328 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
4311 assert_response :success
4329 assert_response :success
4312
4330
4313 assert_select 'select[name=?]', 'issue[project_id]' do
4331 assert_select 'select[name=?]', 'issue[project_id]' do
4314 assert_select 'option[value=""]', 0
4332 assert_select 'option[value=""]', 0
4315 assert_select 'option[value="2"]'
4333 assert_select 'option[value="2"]'
4316 end
4334 end
4317 end
4335 end
4318
4336
4319 def test_bulk_copy_to_another_project
4337 def test_bulk_copy_to_another_project
4320 @request.session[:user_id] = 2
4338 @request.session[:user_id] = 2
4321 assert_difference 'Issue.count', 2 do
4339 assert_difference 'Issue.count', 2 do
4322 assert_no_difference 'Project.find(1).issues.count' do
4340 assert_no_difference 'Project.find(1).issues.count' do
4323 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
4341 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
4324 end
4342 end
4325 end
4343 end
4326 assert_redirected_to '/projects/ecookbook/issues'
4344 assert_redirected_to '/projects/ecookbook/issues'
4327
4345
4328 copies = Issue.order('id DESC').limit(issues.size)
4346 copies = Issue.order('id DESC').limit(issues.size)
4329 copies.each do |copy|
4347 copies.each do |copy|
4330 assert_equal 2, copy.project_id
4348 assert_equal 2, copy.project_id
4331 end
4349 end
4332 end
4350 end
4333
4351
4334 def test_bulk_copy_without_add_issues_permission_should_be_allowed_on_project_with_permission
4352 def test_bulk_copy_without_add_issues_permission_should_be_allowed_on_project_with_permission
4335 user = setup_user_with_copy_but_not_add_permission
4353 user = setup_user_with_copy_but_not_add_permission
4336 @request.session[:user_id] = user.id
4354 @request.session[:user_id] = user.id
4337
4355
4338 assert_difference 'Issue.count', 3 do
4356 assert_difference 'Issue.count', 3 do
4339 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '2'}, :copy => '1'
4357 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '2'}, :copy => '1'
4340 assert_response 302
4358 assert_response 302
4341 end
4359 end
4342 end
4360 end
4343
4361
4344 def test_bulk_copy_on_same_project_without_add_issues_permission_should_be_denied
4362 def test_bulk_copy_on_same_project_without_add_issues_permission_should_be_denied
4345 user = setup_user_with_copy_but_not_add_permission
4363 user = setup_user_with_copy_but_not_add_permission
4346 @request.session[:user_id] = user.id
4364 @request.session[:user_id] = user.id
4347
4365
4348 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => ''}, :copy => '1'
4366 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => ''}, :copy => '1'
4349 assert_response 403
4367 assert_response 403
4350 end
4368 end
4351
4369
4352 def test_bulk_copy_on_different_project_without_add_issues_permission_should_be_denied
4370 def test_bulk_copy_on_different_project_without_add_issues_permission_should_be_denied
4353 user = setup_user_with_copy_but_not_add_permission
4371 user = setup_user_with_copy_but_not_add_permission
4354 @request.session[:user_id] = user.id
4372 @request.session[:user_id] = user.id
4355
4373
4356 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '1'}, :copy => '1'
4374 post :bulk_update, :ids => [1, 2, 3], :issue => {:project_id => '1'}, :copy => '1'
4357 assert_response 403
4375 assert_response 403
4358 end
4376 end
4359
4377
4360 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
4378 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
4361 @request.session[:user_id] = 2
4379 @request.session[:user_id] = 2
4362 issues = [
4380 issues = [
4363 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
4381 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
4364 :priority_id => 2, :subject => 'issue 1', :author_id => 1,
4382 :priority_id => 2, :subject => 'issue 1', :author_id => 1,
4365 :assigned_to_id => nil),
4383 :assigned_to_id => nil),
4366 Issue.create!(:project_id => 2, :tracker_id => 3, :status_id => 2,
4384 Issue.create!(:project_id => 2, :tracker_id => 3, :status_id => 2,
4367 :priority_id => 1, :subject => 'issue 2', :author_id => 2,
4385 :priority_id => 1, :subject => 'issue 2', :author_id => 2,
4368 :assigned_to_id => 3)
4386 :assigned_to_id => 3)
4369 ]
4387 ]
4370 assert_difference 'Issue.count', issues.size do
4388 assert_difference 'Issue.count', issues.size do
4371 post :bulk_update, :ids => issues.map(&:id), :copy => '1',
4389 post :bulk_update, :ids => issues.map(&:id), :copy => '1',
4372 :issue => {
4390 :issue => {
4373 :project_id => '', :tracker_id => '', :assigned_to_id => '',
4391 :project_id => '', :tracker_id => '', :assigned_to_id => '',
4374 :status_id => '', :start_date => '', :due_date => ''
4392 :status_id => '', :start_date => '', :due_date => ''
4375 }
4393 }
4376 end
4394 end
4377
4395
4378 copies = Issue.order('id DESC').limit(issues.size)
4396 copies = Issue.order('id DESC').limit(issues.size)
4379 issues.each do |orig|
4397 issues.each do |orig|
4380 copy = copies.detect {|c| c.subject == orig.subject}
4398 copy = copies.detect {|c| c.subject == orig.subject}
4381 assert_not_nil copy
4399 assert_not_nil copy
4382 assert_equal orig.project_id, copy.project_id
4400 assert_equal orig.project_id, copy.project_id
4383 assert_equal orig.tracker_id, copy.tracker_id
4401 assert_equal orig.tracker_id, copy.tracker_id
4384 assert_equal orig.status_id, copy.status_id
4402 assert_equal orig.status_id, copy.status_id
4385 assert_equal orig.assigned_to_id, copy.assigned_to_id
4403 assert_equal orig.assigned_to_id, copy.assigned_to_id
4386 assert_equal orig.priority_id, copy.priority_id
4404 assert_equal orig.priority_id, copy.priority_id
4387 end
4405 end
4388 end
4406 end
4389
4407
4390 def test_bulk_copy_should_allow_changing_the_issue_attributes
4408 def test_bulk_copy_should_allow_changing_the_issue_attributes
4391 # Fixes random test failure with Mysql
4409 # Fixes random test failure with Mysql
4392 # where Issue.where(:project_id => 2).limit(2).order('id desc')
4410 # where Issue.where(:project_id => 2).limit(2).order('id desc')
4393 # doesn't return the expected results
4411 # doesn't return the expected results
4394 Issue.where("project_id=2").delete_all
4412 Issue.where("project_id=2").delete_all
4395
4413
4396 @request.session[:user_id] = 2
4414 @request.session[:user_id] = 2
4397 assert_difference 'Issue.count', 2 do
4415 assert_difference 'Issue.count', 2 do
4398 assert_no_difference 'Project.find(1).issues.count' do
4416 assert_no_difference 'Project.find(1).issues.count' do
4399 post :bulk_update, :ids => [1, 2], :copy => '1',
4417 post :bulk_update, :ids => [1, 2], :copy => '1',
4400 :issue => {
4418 :issue => {
4401 :project_id => '2', :tracker_id => '', :assigned_to_id => '2',
4419 :project_id => '2', :tracker_id => '', :assigned_to_id => '2',
4402 :status_id => '1', :start_date => '2009-12-01', :due_date => '2009-12-31'
4420 :status_id => '1', :start_date => '2009-12-01', :due_date => '2009-12-31'
4403 }
4421 }
4404 end
4422 end
4405 end
4423 end
4406
4424
4407 copied_issues = Issue.where(:project_id => 2).limit(2).order('id desc').to_a
4425 copied_issues = Issue.where(:project_id => 2).limit(2).order('id desc').to_a
4408 assert_equal 2, copied_issues.size
4426 assert_equal 2, copied_issues.size
4409 copied_issues.each do |issue|
4427 copied_issues.each do |issue|
4410 assert_equal 2, issue.project_id, "Project is incorrect"
4428 assert_equal 2, issue.project_id, "Project is incorrect"
4411 assert_equal 2, issue.assigned_to_id, "Assigned to is incorrect"
4429 assert_equal 2, issue.assigned_to_id, "Assigned to is incorrect"
4412 assert_equal 1, issue.status_id, "Status is incorrect"
4430 assert_equal 1, issue.status_id, "Status is incorrect"
4413 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
4431 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
4414 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
4432 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
4415 end
4433 end
4416 end
4434 end
4417
4435
4418 def test_bulk_copy_should_allow_adding_a_note
4436 def test_bulk_copy_should_allow_adding_a_note
4419 @request.session[:user_id] = 2
4437 @request.session[:user_id] = 2
4420 assert_difference 'Issue.count', 1 do
4438 assert_difference 'Issue.count', 1 do
4421 post :bulk_update, :ids => [1], :copy => '1',
4439 post :bulk_update, :ids => [1], :copy => '1',
4422 :notes => 'Copying one issue',
4440 :notes => 'Copying one issue',
4423 :issue => {
4441 :issue => {
4424 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
4442 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
4425 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
4443 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
4426 }
4444 }
4427 end
4445 end
4428 issue = Issue.order('id DESC').first
4446 issue = Issue.order('id DESC').first
4429 assert_equal 1, issue.journals.size
4447 assert_equal 1, issue.journals.size
4430 journal = issue.journals.first
4448 journal = issue.journals.first
4431 assert_equal 'Copying one issue', journal.notes
4449 assert_equal 'Copying one issue', journal.notes
4432 end
4450 end
4433
4451
4434 def test_bulk_copy_should_allow_not_copying_the_attachments
4452 def test_bulk_copy_should_allow_not_copying_the_attachments
4435 attachment_count = Issue.find(3).attachments.size
4453 attachment_count = Issue.find(3).attachments.size
4436 assert attachment_count > 0
4454 assert attachment_count > 0
4437 @request.session[:user_id] = 2
4455 @request.session[:user_id] = 2
4438
4456
4439 assert_difference 'Issue.count', 1 do
4457 assert_difference 'Issue.count', 1 do
4440 assert_no_difference 'Attachment.count' do
4458 assert_no_difference 'Attachment.count' do
4441 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '0',
4459 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '0',
4442 :issue => {
4460 :issue => {
4443 :project_id => ''
4461 :project_id => ''
4444 }
4462 }
4445 end
4463 end
4446 end
4464 end
4447 end
4465 end
4448
4466
4449 def test_bulk_copy_should_allow_copying_the_attachments
4467 def test_bulk_copy_should_allow_copying_the_attachments
4450 attachment_count = Issue.find(3).attachments.size
4468 attachment_count = Issue.find(3).attachments.size
4451 assert attachment_count > 0
4469 assert attachment_count > 0
4452 @request.session[:user_id] = 2
4470 @request.session[:user_id] = 2
4453
4471
4454 assert_difference 'Issue.count', 1 do
4472 assert_difference 'Issue.count', 1 do
4455 assert_difference 'Attachment.count', attachment_count do
4473 assert_difference 'Attachment.count', attachment_count do
4456 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '1',
4474 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '1',
4457 :issue => {
4475 :issue => {
4458 :project_id => ''
4476 :project_id => ''
4459 }
4477 }
4460 end
4478 end
4461 end
4479 end
4462 end
4480 end
4463
4481
4464 def test_bulk_copy_should_add_relations_with_copied_issues
4482 def test_bulk_copy_should_add_relations_with_copied_issues
4465 @request.session[:user_id] = 2
4483 @request.session[:user_id] = 2
4466
4484
4467 assert_difference 'Issue.count', 2 do
4485 assert_difference 'Issue.count', 2 do
4468 assert_difference 'IssueRelation.count', 2 do
4486 assert_difference 'IssueRelation.count', 2 do
4469 post :bulk_update, :ids => [1, 3], :copy => '1', :link_copy => '1',
4487 post :bulk_update, :ids => [1, 3], :copy => '1', :link_copy => '1',
4470 :issue => {
4488 :issue => {
4471 :project_id => '1'
4489 :project_id => '1'
4472 }
4490 }
4473 end
4491 end
4474 end
4492 end
4475 end
4493 end
4476
4494
4477 def test_bulk_copy_should_allow_not_copying_the_subtasks
4495 def test_bulk_copy_should_allow_not_copying_the_subtasks
4478 issue = Issue.generate_with_descendants!
4496 issue = Issue.generate_with_descendants!
4479 @request.session[:user_id] = 2
4497 @request.session[:user_id] = 2
4480
4498
4481 assert_difference 'Issue.count', 1 do
4499 assert_difference 'Issue.count', 1 do
4482 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '0',
4500 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '0',
4483 :issue => {
4501 :issue => {
4484 :project_id => ''
4502 :project_id => ''
4485 }
4503 }
4486 end
4504 end
4487 end
4505 end
4488
4506
4489 def test_bulk_copy_should_allow_copying_the_subtasks
4507 def test_bulk_copy_should_allow_copying_the_subtasks
4490 issue = Issue.generate_with_descendants!
4508 issue = Issue.generate_with_descendants!
4491 count = issue.descendants.count
4509 count = issue.descendants.count
4492 @request.session[:user_id] = 2
4510 @request.session[:user_id] = 2
4493
4511
4494 assert_difference 'Issue.count', count+1 do
4512 assert_difference 'Issue.count', count+1 do
4495 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '1',
4513 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '1',
4496 :issue => {
4514 :issue => {
4497 :project_id => ''
4515 :project_id => ''
4498 }
4516 }
4499 end
4517 end
4500 copy = Issue.where(:parent_id => nil).order("id DESC").first
4518 copy = Issue.where(:parent_id => nil).order("id DESC").first
4501 assert_equal count, copy.descendants.count
4519 assert_equal count, copy.descendants.count
4502 end
4520 end
4503
4521
4504 def test_bulk_copy_should_not_copy_selected_subtasks_twice
4522 def test_bulk_copy_should_not_copy_selected_subtasks_twice
4505 issue = Issue.generate_with_descendants!
4523 issue = Issue.generate_with_descendants!
4506 count = issue.descendants.count
4524 count = issue.descendants.count
4507 @request.session[:user_id] = 2
4525 @request.session[:user_id] = 2
4508
4526
4509 assert_difference 'Issue.count', count+1 do
4527 assert_difference 'Issue.count', count+1 do
4510 post :bulk_update, :ids => issue.self_and_descendants.map(&:id), :copy => '1', :copy_subtasks => '1',
4528 post :bulk_update, :ids => issue.self_and_descendants.map(&:id), :copy => '1', :copy_subtasks => '1',
4511 :issue => {
4529 :issue => {
4512 :project_id => ''
4530 :project_id => ''
4513 }
4531 }
4514 end
4532 end
4515 copy = Issue.where(:parent_id => nil).order("id DESC").first
4533 copy = Issue.where(:parent_id => nil).order("id DESC").first
4516 assert_equal count, copy.descendants.count
4534 assert_equal count, copy.descendants.count
4517 end
4535 end
4518
4536
4519 def test_bulk_copy_to_another_project_should_follow_when_needed
4537 def test_bulk_copy_to_another_project_should_follow_when_needed
4520 @request.session[:user_id] = 2
4538 @request.session[:user_id] = 2
4521 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
4539 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
4522 issue = Issue.order('id DESC').first
4540 issue = Issue.order('id DESC').first
4523 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
4541 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
4524 end
4542 end
4525
4543
4526 def test_bulk_copy_with_all_failures_should_display_errors
4544 def test_bulk_copy_with_all_failures_should_display_errors
4527 @request.session[:user_id] = 2
4545 @request.session[:user_id] = 2
4528 post :bulk_update, :ids => [1, 2], :copy => '1', :issue => {:start_date => 'foo'}
4546 post :bulk_update, :ids => [1, 2], :copy => '1', :issue => {:start_date => 'foo'}
4529
4547
4530 assert_response :success
4548 assert_response :success
4531 end
4549 end
4532
4550
4533 def test_destroy_issue_with_no_time_entries
4551 def test_destroy_issue_with_no_time_entries
4534 assert_nil TimeEntry.find_by_issue_id(2)
4552 assert_nil TimeEntry.find_by_issue_id(2)
4535 @request.session[:user_id] = 2
4553 @request.session[:user_id] = 2
4536
4554
4537 assert_difference 'Issue.count', -1 do
4555 assert_difference 'Issue.count', -1 do
4538 delete :destroy, :id => 2
4556 delete :destroy, :id => 2
4539 end
4557 end
4540 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4558 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4541 assert_nil Issue.find_by_id(2)
4559 assert_nil Issue.find_by_id(2)
4542 end
4560 end
4543
4561
4544 def test_destroy_issues_with_time_entries
4562 def test_destroy_issues_with_time_entries
4545 @request.session[:user_id] = 2
4563 @request.session[:user_id] = 2
4546
4564
4547 assert_no_difference 'Issue.count' do
4565 assert_no_difference 'Issue.count' do
4548 delete :destroy, :ids => [1, 3]
4566 delete :destroy, :ids => [1, 3]
4549 end
4567 end
4550 assert_response :success
4568 assert_response :success
4551
4569
4552 assert_select 'form' do
4570 assert_select 'form' do
4553 assert_select 'input[name=_method][value=delete]'
4571 assert_select 'input[name=_method][value=delete]'
4554 end
4572 end
4555 end
4573 end
4556
4574
4557 def test_destroy_issues_and_destroy_time_entries
4575 def test_destroy_issues_and_destroy_time_entries
4558 @request.session[:user_id] = 2
4576 @request.session[:user_id] = 2
4559
4577
4560 assert_difference 'Issue.count', -2 do
4578 assert_difference 'Issue.count', -2 do
4561 assert_difference 'TimeEntry.count', -3 do
4579 assert_difference 'TimeEntry.count', -3 do
4562 delete :destroy, :ids => [1, 3], :todo => 'destroy'
4580 delete :destroy, :ids => [1, 3], :todo => 'destroy'
4563 end
4581 end
4564 end
4582 end
4565 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4583 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4566 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4584 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4567 assert_nil TimeEntry.find_by_id([1, 2])
4585 assert_nil TimeEntry.find_by_id([1, 2])
4568 end
4586 end
4569
4587
4570 def test_destroy_issues_and_assign_time_entries_to_project
4588 def test_destroy_issues_and_assign_time_entries_to_project
4571 @request.session[:user_id] = 2
4589 @request.session[:user_id] = 2
4572
4590
4573 assert_difference 'Issue.count', -2 do
4591 assert_difference 'Issue.count', -2 do
4574 assert_no_difference 'TimeEntry.count' do
4592 assert_no_difference 'TimeEntry.count' do
4575 delete :destroy, :ids => [1, 3], :todo => 'nullify'
4593 delete :destroy, :ids => [1, 3], :todo => 'nullify'
4576 end
4594 end
4577 end
4595 end
4578 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4596 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4579 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4597 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4580 assert_nil TimeEntry.find(1).issue_id
4598 assert_nil TimeEntry.find(1).issue_id
4581 assert_nil TimeEntry.find(2).issue_id
4599 assert_nil TimeEntry.find(2).issue_id
4582 end
4600 end
4583
4601
4584 def test_destroy_issues_and_reassign_time_entries_to_another_issue
4602 def test_destroy_issues_and_reassign_time_entries_to_another_issue
4585 @request.session[:user_id] = 2
4603 @request.session[:user_id] = 2
4586
4604
4587 assert_difference 'Issue.count', -2 do
4605 assert_difference 'Issue.count', -2 do
4588 assert_no_difference 'TimeEntry.count' do
4606 assert_no_difference 'TimeEntry.count' do
4589 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
4607 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
4590 end
4608 end
4591 end
4609 end
4592 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4610 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
4593 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4611 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
4594 assert_equal 2, TimeEntry.find(1).issue_id
4612 assert_equal 2, TimeEntry.find(1).issue_id
4595 assert_equal 2, TimeEntry.find(2).issue_id
4613 assert_equal 2, TimeEntry.find(2).issue_id
4596 end
4614 end
4597
4615
4598 def test_destroy_issues_and_reassign_time_entries_to_an_invalid_issue_should_fail
4616 def test_destroy_issues_and_reassign_time_entries_to_an_invalid_issue_should_fail
4599 @request.session[:user_id] = 2
4617 @request.session[:user_id] = 2
4600
4618
4601 assert_no_difference 'Issue.count' do
4619 assert_no_difference 'Issue.count' do
4602 assert_no_difference 'TimeEntry.count' do
4620 assert_no_difference 'TimeEntry.count' do
4603 # try to reassign time to an issue of another project
4621 # try to reassign time to an issue of another project
4604 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 4
4622 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 4
4605 end
4623 end
4606 end
4624 end
4607 assert_response :success
4625 assert_response :success
4608 end
4626 end
4609
4627
4610 def test_destroy_issues_from_different_projects
4628 def test_destroy_issues_from_different_projects
4611 @request.session[:user_id] = 2
4629 @request.session[:user_id] = 2
4612
4630
4613 assert_difference 'Issue.count', -3 do
4631 assert_difference 'Issue.count', -3 do
4614 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
4632 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
4615 end
4633 end
4616 assert_redirected_to :controller => 'issues', :action => 'index'
4634 assert_redirected_to :controller => 'issues', :action => 'index'
4617 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
4635 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
4618 end
4636 end
4619
4637
4620 def test_destroy_parent_and_child_issues
4638 def test_destroy_parent_and_child_issues
4621 parent = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Parent Issue')
4639 parent = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Parent Issue')
4622 child = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Child Issue', :parent_issue_id => parent.id)
4640 child = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Child Issue', :parent_issue_id => parent.id)
4623 assert child.is_descendant_of?(parent.reload)
4641 assert child.is_descendant_of?(parent.reload)
4624
4642
4625 @request.session[:user_id] = 2
4643 @request.session[:user_id] = 2
4626 assert_difference 'Issue.count', -2 do
4644 assert_difference 'Issue.count', -2 do
4627 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
4645 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
4628 end
4646 end
4629 assert_response 302
4647 assert_response 302
4630 end
4648 end
4631
4649
4632 def test_destroy_invalid_should_respond_with_404
4650 def test_destroy_invalid_should_respond_with_404
4633 @request.session[:user_id] = 2
4651 @request.session[:user_id] = 2
4634 assert_no_difference 'Issue.count' do
4652 assert_no_difference 'Issue.count' do
4635 delete :destroy, :id => 999
4653 delete :destroy, :id => 999
4636 end
4654 end
4637 assert_response 404
4655 assert_response 404
4638 end
4656 end
4639
4657
4640 def test_destroy_with_permission_on_tracker_should_be_allowed
4658 def test_destroy_with_permission_on_tracker_should_be_allowed
4641 role = Role.find(1)
4659 role = Role.find(1)
4642 role.set_permission_trackers :delete_issues, [1]
4660 role.set_permission_trackers :delete_issues, [1]
4643 role.save!
4661 role.save!
4644 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
4662 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
4645
4663
4646 @request.session[:user_id] = 2
4664 @request.session[:user_id] = 2
4647 assert_difference 'Issue.count', -1 do
4665 assert_difference 'Issue.count', -1 do
4648 delete :destroy, :id => issue.id
4666 delete :destroy, :id => issue.id
4649 end
4667 end
4650 assert_response 302
4668 assert_response 302
4651 end
4669 end
4652
4670
4653 def test_destroy_without_permission_on_tracker_should_be_denied
4671 def test_destroy_without_permission_on_tracker_should_be_denied
4654 role = Role.find(1)
4672 role = Role.find(1)
4655 role.set_permission_trackers :delete_issues, [2]
4673 role.set_permission_trackers :delete_issues, [2]
4656 role.save!
4674 role.save!
4657 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
4675 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
4658
4676
4659 @request.session[:user_id] = 2
4677 @request.session[:user_id] = 2
4660 assert_no_difference 'Issue.count' do
4678 assert_no_difference 'Issue.count' do
4661 delete :destroy, :id => issue.id
4679 delete :destroy, :id => issue.id
4662 end
4680 end
4663 assert_response 403
4681 assert_response 403
4664 end
4682 end
4665
4683
4666 def test_default_search_scope
4684 def test_default_search_scope
4667 get :index
4685 get :index
4668
4686
4669 assert_select 'div#quick-search form' do
4687 assert_select 'div#quick-search form' do
4670 assert_select 'input[name=issues][value="1"][type=hidden]'
4688 assert_select 'input[name=issues][value="1"][type=hidden]'
4671 end
4689 end
4672 end
4690 end
4673
4691
4674 def setup_user_with_copy_but_not_add_permission
4692 def setup_user_with_copy_but_not_add_permission
4675 Role.all.each {|r| r.remove_permission! :add_issues}
4693 Role.all.each {|r| r.remove_permission! :add_issues}
4676 Role.find_by_name('Manager').add_permission! :add_issues
4694 Role.find_by_name('Manager').add_permission! :add_issues
4677 user = User.generate!
4695 user = User.generate!
4678 User.add_to_project(user, Project.find(1), Role.find_by_name('Developer'))
4696 User.add_to_project(user, Project.find(1), Role.find_by_name('Developer'))
4679 User.add_to_project(user, Project.find(2), Role.find_by_name('Manager'))
4697 User.add_to_project(user, Project.find(2), Role.find_by_name('Manager'))
4680 user
4698 user
4681 end
4699 end
4682 end
4700 end
General Comments 0
You need to be logged in to leave comments. Login now