@@ -1,517 +1,531 | |||
|
1 | 1 | # Redmine - project management software |
|
2 | 2 | # Copyright (C) 2006-2013 Jean-Philippe Lang |
|
3 | 3 | # |
|
4 | 4 | # This program is free software; you can redistribute it and/or |
|
5 | 5 | # modify it under the terms of the GNU General Public License |
|
6 | 6 | # as published by the Free Software Foundation; either version 2 |
|
7 | 7 | # of the License, or (at your option) any later version. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU General Public License |
|
15 | 15 | # along with this program; if not, write to the Free Software |
|
16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | 17 | |
|
18 | 18 | class MailHandler < ActionMailer::Base |
|
19 | 19 | include ActionView::Helpers::SanitizeHelper |
|
20 | 20 | include Redmine::I18n |
|
21 | 21 | |
|
22 | 22 | class UnauthorizedAction < StandardError; end |
|
23 | 23 | class MissingInformation < StandardError; end |
|
24 | 24 | |
|
25 | 25 | attr_reader :email, :user |
|
26 | 26 | |
|
27 | 27 | def self.receive(email, options={}) |
|
28 | 28 | @@handler_options = options.dup |
|
29 | 29 | |
|
30 | 30 | @@handler_options[:issue] ||= {} |
|
31 | 31 | |
|
32 | 32 | if @@handler_options[:allow_override].is_a?(String) |
|
33 | 33 | @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip) |
|
34 | 34 | end |
|
35 | 35 | @@handler_options[:allow_override] ||= [] |
|
36 | 36 | # Project needs to be overridable if not specified |
|
37 | 37 | @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project) |
|
38 | 38 | # Status overridable by default |
|
39 | 39 | @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status) |
|
40 | 40 | |
|
41 | 41 | @@handler_options[:no_account_notice] = (@@handler_options[:no_account_notice].to_s == '1') |
|
42 | 42 | @@handler_options[:no_notification] = (@@handler_options[:no_notification].to_s == '1') |
|
43 | 43 | @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1') |
|
44 | 44 | |
|
45 | 45 | email.force_encoding('ASCII-8BIT') if email.respond_to?(:force_encoding) |
|
46 | 46 | super(email) |
|
47 | 47 | end |
|
48 | 48 | |
|
49 | 49 | # Extracts MailHandler options from environment variables |
|
50 | 50 | # Use when receiving emails with rake tasks |
|
51 | 51 | def self.extract_options_from_env(env) |
|
52 | 52 | options = {:issue => {}} |
|
53 | 53 | %w(project status tracker category priority).each do |option| |
|
54 | 54 | options[:issue][option.to_sym] = env[option] if env[option] |
|
55 | 55 | end |
|
56 | 56 | %w(allow_override unknown_user no_permission_check no_account_notice default_group).each do |option| |
|
57 | 57 | options[option.to_sym] = env[option] if env[option] |
|
58 | 58 | end |
|
59 | 59 | options |
|
60 | 60 | end |
|
61 | 61 | |
|
62 | 62 | def logger |
|
63 | 63 | Rails.logger |
|
64 | 64 | end |
|
65 | 65 | |
|
66 | 66 | cattr_accessor :ignored_emails_headers |
|
67 | 67 | @@ignored_emails_headers = { |
|
68 | 68 | 'X-Auto-Response-Suppress' => 'oof', |
|
69 | 69 | 'Auto-Submitted' => /^auto-/ |
|
70 | 70 | } |
|
71 | 71 | |
|
72 | 72 | # Processes incoming emails |
|
73 | 73 | # Returns the created object (eg. an issue, a message) or false |
|
74 | 74 | def receive(email) |
|
75 | 75 | @email = email |
|
76 | 76 | sender_email = email.from.to_a.first.to_s.strip |
|
77 | 77 | # Ignore emails received from the application emission address to avoid hell cycles |
|
78 | 78 | if sender_email.downcase == Setting.mail_from.to_s.strip.downcase |
|
79 | 79 | if logger |
|
80 | 80 | logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]" |
|
81 | 81 | end |
|
82 | 82 | return false |
|
83 | 83 | end |
|
84 | 84 | # Ignore auto generated emails |
|
85 | 85 | self.class.ignored_emails_headers.each do |key, ignored_value| |
|
86 | 86 | value = email.header[key] |
|
87 | 87 | if value |
|
88 | 88 | value = value.to_s.downcase |
|
89 | 89 | if (ignored_value.is_a?(Regexp) && value.match(ignored_value)) || value == ignored_value |
|
90 | 90 | if logger |
|
91 | 91 | logger.info "MailHandler: ignoring email with #{key}:#{value} header" |
|
92 | 92 | end |
|
93 | 93 | return false |
|
94 | 94 | end |
|
95 | 95 | end |
|
96 | 96 | end |
|
97 | 97 | @user = User.find_by_mail(sender_email) if sender_email.present? |
|
98 | 98 | if @user && !@user.active? |
|
99 | 99 | if logger |
|
100 | 100 | logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]" |
|
101 | 101 | end |
|
102 | 102 | return false |
|
103 | 103 | end |
|
104 | 104 | if @user.nil? |
|
105 | 105 | # Email was submitted by an unknown user |
|
106 | 106 | case @@handler_options[:unknown_user] |
|
107 | 107 | when 'accept' |
|
108 | 108 | @user = User.anonymous |
|
109 | 109 | when 'create' |
|
110 | 110 | @user = create_user_from_email |
|
111 | 111 | if @user |
|
112 | 112 | if logger |
|
113 | 113 | logger.info "MailHandler: [#{@user.login}] account created" |
|
114 | 114 | end |
|
115 | 115 | add_user_to_group(@@handler_options[:default_group]) |
|
116 | 116 | unless @@handler_options[:no_account_notice] |
|
117 | 117 | Mailer.account_information(@user, @user.password).deliver |
|
118 | 118 | end |
|
119 | 119 | else |
|
120 | 120 | if logger |
|
121 | 121 | logger.error "MailHandler: could not create account for [#{sender_email}]" |
|
122 | 122 | end |
|
123 | 123 | return false |
|
124 | 124 | end |
|
125 | 125 | else |
|
126 | 126 | # Default behaviour, emails from unknown users are ignored |
|
127 | 127 | if logger |
|
128 | 128 | logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]" |
|
129 | 129 | end |
|
130 | 130 | return false |
|
131 | 131 | end |
|
132 | 132 | end |
|
133 | 133 | User.current = @user |
|
134 | 134 | dispatch |
|
135 | 135 | end |
|
136 | 136 | |
|
137 | 137 | private |
|
138 | 138 | |
|
139 | 139 | MESSAGE_ID_RE = %r{^<?redmine\.([a-z0-9_]+)\-(\d+)\.\d+(\.[a-f0-9]+)?@} |
|
140 | 140 | ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]} |
|
141 | 141 | MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]} |
|
142 | 142 | |
|
143 | 143 | def dispatch |
|
144 | 144 | headers = [email.in_reply_to, email.references].flatten.compact |
|
145 | 145 | subject = email.subject.to_s |
|
146 | 146 | if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE} |
|
147 | 147 | klass, object_id = $1, $2.to_i |
|
148 | 148 | method_name = "receive_#{klass}_reply" |
|
149 | 149 | if self.class.private_instance_methods.collect(&:to_s).include?(method_name) |
|
150 | 150 | send method_name, object_id |
|
151 | 151 | else |
|
152 | 152 | # ignoring it |
|
153 | 153 | end |
|
154 | 154 | elsif m = subject.match(ISSUE_REPLY_SUBJECT_RE) |
|
155 | 155 | receive_issue_reply(m[1].to_i) |
|
156 | 156 | elsif m = subject.match(MESSAGE_REPLY_SUBJECT_RE) |
|
157 | 157 | receive_message_reply(m[1].to_i) |
|
158 | 158 | else |
|
159 | 159 | dispatch_to_default |
|
160 | 160 | end |
|
161 | 161 | rescue ActiveRecord::RecordInvalid => e |
|
162 | 162 | # TODO: send a email to the user |
|
163 | 163 | logger.error e.message if logger |
|
164 | 164 | false |
|
165 | 165 | rescue MissingInformation => e |
|
166 | 166 | logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger |
|
167 | 167 | false |
|
168 | 168 | rescue UnauthorizedAction => e |
|
169 | 169 | logger.error "MailHandler: unauthorized attempt from #{user}" if logger |
|
170 | 170 | false |
|
171 | 171 | end |
|
172 | 172 | |
|
173 | 173 | def dispatch_to_default |
|
174 | 174 | receive_issue |
|
175 | 175 | end |
|
176 | 176 | |
|
177 | 177 | # Creates a new issue |
|
178 | 178 | def receive_issue |
|
179 | 179 | project = target_project |
|
180 | 180 | # check permission |
|
181 | 181 | unless @@handler_options[:no_permission_check] |
|
182 | 182 | raise UnauthorizedAction unless user.allowed_to?(:add_issues, project) |
|
183 | 183 | end |
|
184 | 184 | |
|
185 | 185 | issue = Issue.new(:author => user, :project => project) |
|
186 | 186 | issue.safe_attributes = issue_attributes_from_keywords(issue) |
|
187 | 187 | issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} |
|
188 | 188 | issue.subject = cleaned_up_subject |
|
189 | 189 | if issue.subject.blank? |
|
190 | 190 | issue.subject = '(no subject)' |
|
191 | 191 | end |
|
192 | 192 | issue.description = cleaned_up_text_body |
|
193 | 193 | |
|
194 | 194 | # add To and Cc as watchers before saving so the watchers can reply to Redmine |
|
195 | 195 | add_watchers(issue) |
|
196 | 196 | issue.save! |
|
197 | 197 | add_attachments(issue) |
|
198 | 198 | logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger |
|
199 | 199 | issue |
|
200 | 200 | end |
|
201 | 201 | |
|
202 | 202 | # Adds a note to an existing issue |
|
203 | 203 | def receive_issue_reply(issue_id, from_journal=nil) |
|
204 | 204 | issue = Issue.find_by_id(issue_id) |
|
205 | 205 | return unless issue |
|
206 | 206 | # check permission |
|
207 | 207 | unless @@handler_options[:no_permission_check] |
|
208 | 208 | unless user.allowed_to?(:add_issue_notes, issue.project) || |
|
209 | 209 | user.allowed_to?(:edit_issues, issue.project) |
|
210 | 210 | raise UnauthorizedAction |
|
211 | 211 | end |
|
212 | 212 | end |
|
213 | 213 | |
|
214 | 214 | # ignore CLI-supplied defaults for new issues |
|
215 | 215 | @@handler_options[:issue].clear |
|
216 | 216 | |
|
217 | 217 | journal = issue.init_journal(user) |
|
218 | 218 | if from_journal && from_journal.private_notes? |
|
219 | 219 | # If the received email was a reply to a private note, make the added note private |
|
220 | 220 | issue.private_notes = true |
|
221 | 221 | end |
|
222 | 222 | issue.safe_attributes = issue_attributes_from_keywords(issue) |
|
223 | 223 | issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} |
|
224 | 224 | journal.notes = cleaned_up_text_body |
|
225 | 225 | add_attachments(issue) |
|
226 | 226 | issue.save! |
|
227 | 227 | if logger |
|
228 | 228 | logger.info "MailHandler: issue ##{issue.id} updated by #{user}" |
|
229 | 229 | end |
|
230 | 230 | journal |
|
231 | 231 | end |
|
232 | 232 | |
|
233 | 233 | # Reply will be added to the issue |
|
234 | 234 | def receive_journal_reply(journal_id) |
|
235 | 235 | journal = Journal.find_by_id(journal_id) |
|
236 | 236 | if journal && journal.journalized_type == 'Issue' |
|
237 | 237 | receive_issue_reply(journal.journalized_id, journal) |
|
238 | 238 | end |
|
239 | 239 | end |
|
240 | 240 | |
|
241 | 241 | # Receives a reply to a forum message |
|
242 | 242 | def receive_message_reply(message_id) |
|
243 | 243 | message = Message.find_by_id(message_id) |
|
244 | 244 | if message |
|
245 | 245 | message = message.root |
|
246 | 246 | |
|
247 | 247 | unless @@handler_options[:no_permission_check] |
|
248 | 248 | raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project) |
|
249 | 249 | end |
|
250 | 250 | |
|
251 | 251 | if !message.locked? |
|
252 | 252 | reply = Message.new(:subject => cleaned_up_subject.gsub(%r{^.*msg\d+\]}, '').strip, |
|
253 | 253 | :content => cleaned_up_text_body) |
|
254 | 254 | reply.author = user |
|
255 | 255 | reply.board = message.board |
|
256 | 256 | message.children << reply |
|
257 | 257 | add_attachments(reply) |
|
258 | 258 | reply |
|
259 | 259 | else |
|
260 | 260 | if logger |
|
261 | 261 | logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic" |
|
262 | 262 | end |
|
263 | 263 | end |
|
264 | 264 | end |
|
265 | 265 | end |
|
266 | 266 | |
|
267 | 267 | def add_attachments(obj) |
|
268 | 268 | if email.attachments && email.attachments.any? |
|
269 | 269 | email.attachments.each do |attachment| |
|
270 | next unless accept_attachment?(attachment) | |
|
270 | 271 | obj.attachments << Attachment.create(:container => obj, |
|
271 | 272 | :file => attachment.decoded, |
|
272 | 273 | :filename => attachment.filename, |
|
273 | 274 | :author => user, |
|
274 | 275 | :content_type => attachment.mime_type) |
|
275 | 276 | end |
|
276 | 277 | end |
|
277 | 278 | end |
|
278 | 279 | |
|
280 | # Returns false if the +attachment+ of the incoming email should be ignored | |
|
281 | def accept_attachment?(attachment) | |
|
282 | @excluded ||= Setting.mail_handler_excluded_filenames.to_s.split(',').map(&:strip).reject(&:blank?) | |
|
283 | @excluded.each do |pattern| | |
|
284 | regexp = %r{\A#{Regexp.escape(pattern).gsub("\\*", ".*")}\z}i | |
|
285 | if attachment.filename.to_s =~ regexp | |
|
286 | logger.info "MailHandler: ignoring attachment #{attachment.filename} matching #{pattern}" | |
|
287 | return false | |
|
288 | end | |
|
289 | end | |
|
290 | true | |
|
291 | end | |
|
292 | ||
|
279 | 293 | # Adds To and Cc as watchers of the given object if the sender has the |
|
280 | 294 | # appropriate permission |
|
281 | 295 | def add_watchers(obj) |
|
282 | 296 | if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project) |
|
283 | 297 | addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase} |
|
284 | 298 | unless addresses.empty? |
|
285 | 299 | watchers = User.active.where('LOWER(mail) IN (?)', addresses).all |
|
286 | 300 | watchers.each {|w| obj.add_watcher(w)} |
|
287 | 301 | end |
|
288 | 302 | end |
|
289 | 303 | end |
|
290 | 304 | |
|
291 | 305 | def get_keyword(attr, options={}) |
|
292 | 306 | @keywords ||= {} |
|
293 | 307 | if @keywords.has_key?(attr) |
|
294 | 308 | @keywords[attr] |
|
295 | 309 | else |
|
296 | 310 | @keywords[attr] = begin |
|
297 | 311 | if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && |
|
298 | 312 | (v = extract_keyword!(plain_text_body, attr, options[:format])) |
|
299 | 313 | v |
|
300 | 314 | elsif !@@handler_options[:issue][attr].blank? |
|
301 | 315 | @@handler_options[:issue][attr] |
|
302 | 316 | end |
|
303 | 317 | end |
|
304 | 318 | end |
|
305 | 319 | end |
|
306 | 320 | |
|
307 | 321 | # Destructively extracts the value for +attr+ in +text+ |
|
308 | 322 | # Returns nil if no matching keyword found |
|
309 | 323 | def extract_keyword!(text, attr, format=nil) |
|
310 | 324 | keys = [attr.to_s.humanize] |
|
311 | 325 | if attr.is_a?(Symbol) |
|
312 | 326 | if user && user.language.present? |
|
313 | 327 | keys << l("field_#{attr}", :default => '', :locale => user.language) |
|
314 | 328 | end |
|
315 | 329 | if Setting.default_language.present? |
|
316 | 330 | keys << l("field_#{attr}", :default => '', :locale => Setting.default_language) |
|
317 | 331 | end |
|
318 | 332 | end |
|
319 | 333 | keys.reject! {|k| k.blank?} |
|
320 | 334 | keys.collect! {|k| Regexp.escape(k)} |
|
321 | 335 | format ||= '.+' |
|
322 | 336 | keyword = nil |
|
323 | 337 | regexp = /^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i |
|
324 | 338 | if m = text.match(regexp) |
|
325 | 339 | keyword = m[2].strip |
|
326 | 340 | text.gsub!(regexp, '') |
|
327 | 341 | end |
|
328 | 342 | keyword |
|
329 | 343 | end |
|
330 | 344 | |
|
331 | 345 | def target_project |
|
332 | 346 | # TODO: other ways to specify project: |
|
333 | 347 | # * parse the email To field |
|
334 | 348 | # * specific project (eg. Setting.mail_handler_target_project) |
|
335 | 349 | target = Project.find_by_identifier(get_keyword(:project)) |
|
336 | 350 | if target.nil? |
|
337 | 351 | # Invalid project keyword, use the project specified as the default one |
|
338 | 352 | default_project = @@handler_options[:issue][:project] |
|
339 | 353 | if default_project.present? |
|
340 | 354 | target = Project.find_by_identifier(default_project) |
|
341 | 355 | end |
|
342 | 356 | end |
|
343 | 357 | raise MissingInformation.new('Unable to determine target project') if target.nil? |
|
344 | 358 | target |
|
345 | 359 | end |
|
346 | 360 | |
|
347 | 361 | # Returns a Hash of issue attributes extracted from keywords in the email body |
|
348 | 362 | def issue_attributes_from_keywords(issue) |
|
349 | 363 | assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_assignee_from_keyword(k, issue) |
|
350 | 364 | |
|
351 | 365 | attrs = { |
|
352 | 366 | 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id), |
|
353 | 367 | 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id), |
|
354 | 368 | 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id), |
|
355 | 369 | 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id), |
|
356 | 370 | 'assigned_to_id' => assigned_to.try(:id), |
|
357 | 371 | 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) && |
|
358 | 372 | issue.project.shared_versions.named(k).first.try(:id), |
|
359 | 373 | 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), |
|
360 | 374 | 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), |
|
361 | 375 | 'estimated_hours' => get_keyword(:estimated_hours, :override => true), |
|
362 | 376 | 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0') |
|
363 | 377 | }.delete_if {|k, v| v.blank? } |
|
364 | 378 | |
|
365 | 379 | if issue.new_record? && attrs['tracker_id'].nil? |
|
366 | 380 | attrs['tracker_id'] = issue.project.trackers.first.try(:id) |
|
367 | 381 | end |
|
368 | 382 | |
|
369 | 383 | attrs |
|
370 | 384 | end |
|
371 | 385 | |
|
372 | 386 | # Returns a Hash of issue custom field values extracted from keywords in the email body |
|
373 | 387 | def custom_field_values_from_keywords(customized) |
|
374 | 388 | customized.custom_field_values.inject({}) do |h, v| |
|
375 | 389 | if keyword = get_keyword(v.custom_field.name, :override => true) |
|
376 | 390 | h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(keyword, customized) |
|
377 | 391 | end |
|
378 | 392 | h |
|
379 | 393 | end |
|
380 | 394 | end |
|
381 | 395 | |
|
382 | 396 | # Returns the text/plain part of the email |
|
383 | 397 | # If not found (eg. HTML-only email), returns the body with tags removed |
|
384 | 398 | def plain_text_body |
|
385 | 399 | return @plain_text_body unless @plain_text_body.nil? |
|
386 | 400 | |
|
387 | 401 | parts = if (text_parts = email.all_parts.select {|p| p.mime_type == 'text/plain'}).present? |
|
388 | 402 | text_parts |
|
389 | 403 | elsif (html_parts = email.all_parts.select {|p| p.mime_type == 'text/html'}).present? |
|
390 | 404 | html_parts |
|
391 | 405 | else |
|
392 | 406 | [email] |
|
393 | 407 | end |
|
394 | 408 | @plain_text_body = parts.map {|p| Redmine::CodesetUtil.to_utf8(p.body.decoded, p.charset)}.join("\r\n") |
|
395 | 409 | |
|
396 | 410 | # strip html tags and remove doctype directive |
|
397 | 411 | if parts.any? {|p| p.mime_type == 'text/html'} |
|
398 | 412 | @plain_text_body = strip_tags(@plain_text_body.strip) |
|
399 | 413 | @plain_text_body.sub! %r{^<!DOCTYPE .*$}, '' |
|
400 | 414 | end |
|
401 | 415 | |
|
402 | 416 | @plain_text_body |
|
403 | 417 | end |
|
404 | 418 | |
|
405 | 419 | def cleaned_up_text_body |
|
406 | 420 | cleanup_body(plain_text_body) |
|
407 | 421 | end |
|
408 | 422 | |
|
409 | 423 | def cleaned_up_subject |
|
410 | 424 | subject = email.subject.to_s |
|
411 | 425 | subject.strip[0,255] |
|
412 | 426 | end |
|
413 | 427 | |
|
414 | 428 | def self.full_sanitizer |
|
415 | 429 | @full_sanitizer ||= HTML::FullSanitizer.new |
|
416 | 430 | end |
|
417 | 431 | |
|
418 | 432 | def self.assign_string_attribute_with_limit(object, attribute, value, limit=nil) |
|
419 | 433 | limit ||= object.class.columns_hash[attribute.to_s].limit || 255 |
|
420 | 434 | value = value.to_s.slice(0, limit) |
|
421 | 435 | object.send("#{attribute}=", value) |
|
422 | 436 | end |
|
423 | 437 | |
|
424 | 438 | # Returns a User from an email address and a full name |
|
425 | 439 | def self.new_user_from_attributes(email_address, fullname=nil) |
|
426 | 440 | user = User.new |
|
427 | 441 | |
|
428 | 442 | # Truncating the email address would result in an invalid format |
|
429 | 443 | user.mail = email_address |
|
430 | 444 | assign_string_attribute_with_limit(user, 'login', email_address, User::LOGIN_LENGTH_LIMIT) |
|
431 | 445 | |
|
432 | 446 | names = fullname.blank? ? email_address.gsub(/@.*$/, '').split('.') : fullname.split |
|
433 | 447 | assign_string_attribute_with_limit(user, 'firstname', names.shift, 30) |
|
434 | 448 | assign_string_attribute_with_limit(user, 'lastname', names.join(' '), 30) |
|
435 | 449 | user.lastname = '-' if user.lastname.blank? |
|
436 | 450 | user.language = Setting.default_language |
|
437 | 451 | user.generate_password = true |
|
438 | 452 | user.mail_notification = 'only_my_events' |
|
439 | 453 | |
|
440 | 454 | unless user.valid? |
|
441 | 455 | user.login = "user#{Redmine::Utils.random_hex(6)}" unless user.errors[:login].blank? |
|
442 | 456 | user.firstname = "-" unless user.errors[:firstname].blank? |
|
443 | 457 | (puts user.errors[:lastname];user.lastname = "-") unless user.errors[:lastname].blank? |
|
444 | 458 | end |
|
445 | 459 | |
|
446 | 460 | user |
|
447 | 461 | end |
|
448 | 462 | |
|
449 | 463 | # Creates a User for the +email+ sender |
|
450 | 464 | # Returns the user or nil if it could not be created |
|
451 | 465 | def create_user_from_email |
|
452 | 466 | from = email.header['from'].to_s |
|
453 | 467 | addr, name = from, nil |
|
454 | 468 | if m = from.match(/^"?(.+?)"?\s+<(.+@.+)>$/) |
|
455 | 469 | addr, name = m[2], m[1] |
|
456 | 470 | end |
|
457 | 471 | if addr.present? |
|
458 | 472 | user = self.class.new_user_from_attributes(addr, name) |
|
459 | 473 | if @@handler_options[:no_notification] |
|
460 | 474 | user.mail_notification = 'none' |
|
461 | 475 | end |
|
462 | 476 | if user.save |
|
463 | 477 | user |
|
464 | 478 | else |
|
465 | 479 | logger.error "MailHandler: failed to create User: #{user.errors.full_messages}" if logger |
|
466 | 480 | nil |
|
467 | 481 | end |
|
468 | 482 | else |
|
469 | 483 | logger.error "MailHandler: failed to create User: no FROM address found" if logger |
|
470 | 484 | nil |
|
471 | 485 | end |
|
472 | 486 | end |
|
473 | 487 | |
|
474 | 488 | # Adds the newly created user to default group |
|
475 | 489 | def add_user_to_group(default_group) |
|
476 | 490 | if default_group.present? |
|
477 | 491 | default_group.split(',').each do |group_name| |
|
478 | 492 | if group = Group.named(group_name).first |
|
479 | 493 | group.users << @user |
|
480 | 494 | elsif logger |
|
481 | 495 | logger.warn "MailHandler: could not add user to [#{group_name}], group not found" |
|
482 | 496 | end |
|
483 | 497 | end |
|
484 | 498 | end |
|
485 | 499 | end |
|
486 | 500 | |
|
487 | 501 | # Removes the email body of text after the truncation configurations. |
|
488 | 502 | def cleanup_body(body) |
|
489 | 503 | delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)} |
|
490 | 504 | unless delimiters.empty? |
|
491 | 505 | regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE) |
|
492 | 506 | body = body.gsub(regex, '') |
|
493 | 507 | end |
|
494 | 508 | body.strip |
|
495 | 509 | end |
|
496 | 510 | |
|
497 | 511 | def find_assignee_from_keyword(keyword, issue) |
|
498 | 512 | keyword = keyword.to_s.downcase |
|
499 | 513 | assignable = issue.assignable_users |
|
500 | 514 | assignee = nil |
|
501 | 515 | assignee ||= assignable.detect {|a| |
|
502 | 516 | a.mail.to_s.downcase == keyword || |
|
503 | 517 | a.login.to_s.downcase == keyword |
|
504 | 518 | } |
|
505 | 519 | if assignee.nil? && keyword.match(/ /) |
|
506 | 520 | firstname, lastname = *(keyword.split) # "First Last Throwaway" |
|
507 | 521 | assignee ||= assignable.detect {|a| |
|
508 | 522 | a.is_a?(User) && a.firstname.to_s.downcase == firstname && |
|
509 | 523 | a.lastname.to_s.downcase == lastname |
|
510 | 524 | } |
|
511 | 525 | end |
|
512 | 526 | if assignee.nil? |
|
513 | 527 | assignee ||= assignable.detect {|a| a.name.downcase == keyword} |
|
514 | 528 | end |
|
515 | 529 | assignee |
|
516 | 530 | end |
|
517 | 531 | end |
@@ -1,23 +1,28 | |||
|
1 | 1 | <%= form_tag({:action => 'edit', :tab => 'mail_handler'}) do %> |
|
2 | 2 | |
|
3 | 3 | <div class="box tabular settings"> |
|
4 | 4 | <p> |
|
5 | 5 | <%= setting_text_area :mail_handler_body_delimiters, :rows => 5 %> |
|
6 | 6 | <em class="info"><%= l(:text_line_separated) %></em> |
|
7 | 7 | </p> |
|
8 | <p> | |
|
9 | <%= setting_text_field :mail_handler_excluded_filenames, :size => 60 %> | |
|
10 | <em class="info"><%= l(:text_comma_separated) %> | |
|
11 | <%= l(:label_example) %>: smime.p7s, *.vcf</em> | |
|
12 | </p> | |
|
8 | 13 | </div> |
|
9 | 14 | |
|
10 | 15 | <div class="box tabular settings"> |
|
11 | 16 | <p><%= setting_check_box :mail_handler_api_enabled, |
|
12 | 17 | :onclick => "if (this.checked) { $('#settings_mail_handler_api_key').removeAttr('disabled'); } else { $('#settings_mail_handler_api_key').attr('disabled', true); }"%></p> |
|
13 | 18 | |
|
14 | 19 | <p><%= setting_text_field :mail_handler_api_key, :size => 30, |
|
15 | 20 | :id => 'settings_mail_handler_api_key', |
|
16 | 21 | :disabled => !Setting.mail_handler_api_enabled? %> |
|
17 | 22 | <%= link_to_function l(:label_generate_key), "if (!$('#settings_mail_handler_api_key').attr('disabled')) { $('#settings_mail_handler_api_key').val(randomKey(20)) }" %> |
|
18 | 23 | </p> |
|
19 | 24 | </div> |
|
20 | 25 | |
|
21 | 26 | <%= submit_tag l(:button_save) %> |
|
22 | 27 | |
|
23 | 28 | <% end %> |
@@ -1,1094 +1,1095 | |||
|
1 | 1 | en: |
|
2 | 2 | # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl) |
|
3 | 3 | direction: ltr |
|
4 | 4 | date: |
|
5 | 5 | formats: |
|
6 | 6 | # Use the strftime parameters for formats. |
|
7 | 7 | # When no format has been given, it uses default. |
|
8 | 8 | # You can provide other formats here if you like! |
|
9 | 9 | default: "%m/%d/%Y" |
|
10 | 10 | short: "%b %d" |
|
11 | 11 | long: "%B %d, %Y" |
|
12 | 12 | |
|
13 | 13 | day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] |
|
14 | 14 | abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat] |
|
15 | 15 | |
|
16 | 16 | # Don't forget the nil at the beginning; there's no such thing as a 0th month |
|
17 | 17 | month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December] |
|
18 | 18 | abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec] |
|
19 | 19 | # Used in date_select and datime_select. |
|
20 | 20 | order: |
|
21 | 21 | - :year |
|
22 | 22 | - :month |
|
23 | 23 | - :day |
|
24 | 24 | |
|
25 | 25 | time: |
|
26 | 26 | formats: |
|
27 | 27 | default: "%m/%d/%Y %I:%M %p" |
|
28 | 28 | time: "%I:%M %p" |
|
29 | 29 | short: "%d %b %H:%M" |
|
30 | 30 | long: "%B %d, %Y %H:%M" |
|
31 | 31 | am: "am" |
|
32 | 32 | pm: "pm" |
|
33 | 33 | |
|
34 | 34 | datetime: |
|
35 | 35 | distance_in_words: |
|
36 | 36 | half_a_minute: "half a minute" |
|
37 | 37 | less_than_x_seconds: |
|
38 | 38 | one: "less than 1 second" |
|
39 | 39 | other: "less than %{count} seconds" |
|
40 | 40 | x_seconds: |
|
41 | 41 | one: "1 second" |
|
42 | 42 | other: "%{count} seconds" |
|
43 | 43 | less_than_x_minutes: |
|
44 | 44 | one: "less than a minute" |
|
45 | 45 | other: "less than %{count} minutes" |
|
46 | 46 | x_minutes: |
|
47 | 47 | one: "1 minute" |
|
48 | 48 | other: "%{count} minutes" |
|
49 | 49 | about_x_hours: |
|
50 | 50 | one: "about 1 hour" |
|
51 | 51 | other: "about %{count} hours" |
|
52 | 52 | x_hours: |
|
53 | 53 | one: "1 hour" |
|
54 | 54 | other: "%{count} hours" |
|
55 | 55 | x_days: |
|
56 | 56 | one: "1 day" |
|
57 | 57 | other: "%{count} days" |
|
58 | 58 | about_x_months: |
|
59 | 59 | one: "about 1 month" |
|
60 | 60 | other: "about %{count} months" |
|
61 | 61 | x_months: |
|
62 | 62 | one: "1 month" |
|
63 | 63 | other: "%{count} months" |
|
64 | 64 | about_x_years: |
|
65 | 65 | one: "about 1 year" |
|
66 | 66 | other: "about %{count} years" |
|
67 | 67 | over_x_years: |
|
68 | 68 | one: "over 1 year" |
|
69 | 69 | other: "over %{count} years" |
|
70 | 70 | almost_x_years: |
|
71 | 71 | one: "almost 1 year" |
|
72 | 72 | other: "almost %{count} years" |
|
73 | 73 | |
|
74 | 74 | number: |
|
75 | 75 | format: |
|
76 | 76 | separator: "." |
|
77 | 77 | delimiter: "" |
|
78 | 78 | precision: 3 |
|
79 | 79 | |
|
80 | 80 | human: |
|
81 | 81 | format: |
|
82 | 82 | delimiter: "" |
|
83 | 83 | precision: 3 |
|
84 | 84 | storage_units: |
|
85 | 85 | format: "%n %u" |
|
86 | 86 | units: |
|
87 | 87 | byte: |
|
88 | 88 | one: "Byte" |
|
89 | 89 | other: "Bytes" |
|
90 | 90 | kb: "KB" |
|
91 | 91 | mb: "MB" |
|
92 | 92 | gb: "GB" |
|
93 | 93 | tb: "TB" |
|
94 | 94 | |
|
95 | 95 | # Used in array.to_sentence. |
|
96 | 96 | support: |
|
97 | 97 | array: |
|
98 | 98 | sentence_connector: "and" |
|
99 | 99 | skip_last_comma: false |
|
100 | 100 | |
|
101 | 101 | activerecord: |
|
102 | 102 | errors: |
|
103 | 103 | template: |
|
104 | 104 | header: |
|
105 | 105 | one: "1 error prohibited this %{model} from being saved" |
|
106 | 106 | other: "%{count} errors prohibited this %{model} from being saved" |
|
107 | 107 | messages: |
|
108 | 108 | inclusion: "is not included in the list" |
|
109 | 109 | exclusion: "is reserved" |
|
110 | 110 | invalid: "is invalid" |
|
111 | 111 | confirmation: "doesn't match confirmation" |
|
112 | 112 | accepted: "must be accepted" |
|
113 | 113 | empty: "can't be empty" |
|
114 | 114 | blank: "can't be blank" |
|
115 | 115 | too_long: "is too long (maximum is %{count} characters)" |
|
116 | 116 | too_short: "is too short (minimum is %{count} characters)" |
|
117 | 117 | wrong_length: "is the wrong length (should be %{count} characters)" |
|
118 | 118 | taken: "has already been taken" |
|
119 | 119 | not_a_number: "is not a number" |
|
120 | 120 | not_a_date: "is not a valid date" |
|
121 | 121 | greater_than: "must be greater than %{count}" |
|
122 | 122 | greater_than_or_equal_to: "must be greater than or equal to %{count}" |
|
123 | 123 | equal_to: "must be equal to %{count}" |
|
124 | 124 | less_than: "must be less than %{count}" |
|
125 | 125 | less_than_or_equal_to: "must be less than or equal to %{count}" |
|
126 | 126 | odd: "must be odd" |
|
127 | 127 | even: "must be even" |
|
128 | 128 | greater_than_start_date: "must be greater than start date" |
|
129 | 129 | not_same_project: "doesn't belong to the same project" |
|
130 | 130 | circular_dependency: "This relation would create a circular dependency" |
|
131 | 131 | cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks" |
|
132 | 132 | earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" |
|
133 | 133 | |
|
134 | 134 | actionview_instancetag_blank_option: Please select |
|
135 | 135 | |
|
136 | 136 | general_text_No: 'No' |
|
137 | 137 | general_text_Yes: 'Yes' |
|
138 | 138 | general_text_no: 'no' |
|
139 | 139 | general_text_yes: 'yes' |
|
140 | 140 | general_lang_name: 'English' |
|
141 | 141 | general_csv_separator: ',' |
|
142 | 142 | general_csv_decimal_separator: '.' |
|
143 | 143 | general_csv_encoding: ISO-8859-1 |
|
144 | 144 | general_pdf_encoding: UTF-8 |
|
145 | 145 | general_first_day_of_week: '7' |
|
146 | 146 | |
|
147 | 147 | notice_account_updated: Account was successfully updated. |
|
148 | 148 | notice_account_invalid_creditentials: Invalid user or password |
|
149 | 149 | notice_account_password_updated: Password was successfully updated. |
|
150 | 150 | notice_account_wrong_password: Wrong password |
|
151 | 151 | notice_account_register_done: Account was successfully created. An email containing the instructions to activate your account was sent to %{email}. |
|
152 | 152 | notice_account_unknown_email: Unknown user. |
|
153 | 153 | 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 | 154 | notice_account_locked: Your account is locked. |
|
155 | 155 | notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password. |
|
156 | 156 | notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you. |
|
157 | 157 | notice_account_activated: Your account has been activated. You can now log in. |
|
158 | 158 | notice_successful_create: Successful creation. |
|
159 | 159 | notice_successful_update: Successful update. |
|
160 | 160 | notice_successful_delete: Successful deletion. |
|
161 | 161 | notice_successful_connection: Successful connection. |
|
162 | 162 | notice_file_not_found: The page you were trying to access doesn't exist or has been removed. |
|
163 | 163 | notice_locking_conflict: Data has been updated by another user. |
|
164 | 164 | notice_not_authorized: You are not authorized to access this page. |
|
165 | 165 | notice_not_authorized_archived_project: The project you're trying to access has been archived. |
|
166 | 166 | notice_email_sent: "An email was sent to %{value}" |
|
167 | 167 | notice_email_error: "An error occurred while sending mail (%{value})" |
|
168 | 168 | notice_feeds_access_key_reseted: Your Atom access key was reset. |
|
169 | 169 | notice_api_access_key_reseted: Your API access key was reset. |
|
170 | 170 | notice_failed_to_save_issues: "Failed to save %{count} issue(s) on %{total} selected: %{ids}." |
|
171 | 171 | notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}." |
|
172 | 172 | notice_failed_to_save_members: "Failed to save member(s): %{errors}." |
|
173 | 173 | notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit." |
|
174 | 174 | notice_account_pending: "Your account was created and is now pending administrator approval." |
|
175 | 175 | notice_default_data_loaded: Default configuration successfully loaded. |
|
176 | 176 | notice_unable_delete_version: Unable to delete version. |
|
177 | 177 | notice_unable_delete_time_entry: Unable to delete time log entry. |
|
178 | 178 | notice_issue_done_ratios_updated: Issue done ratios updated. |
|
179 | 179 | notice_gantt_chart_truncated: "The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})" |
|
180 | 180 | notice_issue_successful_create: "Issue %{id} created." |
|
181 | 181 | notice_issue_update_conflict: "The issue has been updated by an other user while you were editing it." |
|
182 | 182 | notice_account_deleted: "Your account has been permanently deleted." |
|
183 | 183 | notice_user_successful_create: "User %{id} created." |
|
184 | 184 | notice_new_password_must_be_different: The new password must be different from the current password |
|
185 | 185 | |
|
186 | 186 | error_can_t_load_default_data: "Default configuration could not be loaded: %{value}" |
|
187 | 187 | error_scm_not_found: "The entry or revision was not found in the repository." |
|
188 | 188 | error_scm_command_failed: "An error occurred when trying to access the repository: %{value}" |
|
189 | 189 | error_scm_annotate: "The entry does not exist or cannot be annotated." |
|
190 | 190 | error_scm_annotate_big_text_file: "The entry cannot be annotated, as it exceeds the maximum text file size." |
|
191 | 191 | error_issue_not_found_in_project: 'The issue was not found or does not belong to this project' |
|
192 | 192 | error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.' |
|
193 | 193 | error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").' |
|
194 | 194 | error_can_not_delete_custom_field: Unable to delete custom field |
|
195 | 195 | error_can_not_delete_tracker: "This tracker contains issues and cannot be deleted." |
|
196 | 196 | error_can_not_remove_role: "This role is in use and cannot be deleted." |
|
197 | 197 | error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version cannot be reopened' |
|
198 | 198 | error_can_not_archive_project: This project cannot be archived |
|
199 | 199 | error_issue_done_ratios_not_updated: "Issue done ratios not updated." |
|
200 | 200 | error_workflow_copy_source: 'Please select a source tracker or role' |
|
201 | 201 | error_workflow_copy_target: 'Please select target tracker(s) and role(s)' |
|
202 | 202 | error_unable_delete_issue_status: 'Unable to delete issue status' |
|
203 | 203 | error_unable_to_connect: "Unable to connect (%{value})" |
|
204 | 204 | error_attachment_too_big: "This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size})" |
|
205 | 205 | error_session_expired: "Your session has expired. Please login again." |
|
206 | 206 | warning_attachments_not_saved: "%{count} file(s) could not be saved." |
|
207 | 207 | |
|
208 | 208 | mail_subject_lost_password: "Your %{value} password" |
|
209 | 209 | mail_body_lost_password: 'To change your password, click on the following link:' |
|
210 | 210 | mail_subject_register: "Your %{value} account activation" |
|
211 | 211 | mail_body_register: 'To activate your account, click on the following link:' |
|
212 | 212 | mail_body_account_information_external: "You can use your %{value} account to log in." |
|
213 | 213 | mail_body_account_information: Your account information |
|
214 | 214 | mail_subject_account_activation_request: "%{value} account activation request" |
|
215 | 215 | mail_body_account_activation_request: "A new user (%{value}) has registered. The account is pending your approval:" |
|
216 | 216 | mail_subject_reminder: "%{count} issue(s) due in the next %{days} days" |
|
217 | 217 | mail_body_reminder: "%{count} issue(s) that are assigned to you are due in the next %{days} days:" |
|
218 | 218 | mail_subject_wiki_content_added: "'%{id}' wiki page has been added" |
|
219 | 219 | mail_body_wiki_content_added: "The '%{id}' wiki page has been added by %{author}." |
|
220 | 220 | mail_subject_wiki_content_updated: "'%{id}' wiki page has been updated" |
|
221 | 221 | mail_body_wiki_content_updated: "The '%{id}' wiki page has been updated by %{author}." |
|
222 | 222 | |
|
223 | 223 | field_name: Name |
|
224 | 224 | field_description: Description |
|
225 | 225 | field_summary: Summary |
|
226 | 226 | field_is_required: Required |
|
227 | 227 | field_firstname: First name |
|
228 | 228 | field_lastname: Last name |
|
229 | 229 | field_mail: Email |
|
230 | 230 | field_filename: File |
|
231 | 231 | field_filesize: Size |
|
232 | 232 | field_downloads: Downloads |
|
233 | 233 | field_author: Author |
|
234 | 234 | field_created_on: Created |
|
235 | 235 | field_updated_on: Updated |
|
236 | 236 | field_closed_on: Closed |
|
237 | 237 | field_field_format: Format |
|
238 | 238 | field_is_for_all: For all projects |
|
239 | 239 | field_possible_values: Possible values |
|
240 | 240 | field_regexp: Regular expression |
|
241 | 241 | field_min_length: Minimum length |
|
242 | 242 | field_max_length: Maximum length |
|
243 | 243 | field_value: Value |
|
244 | 244 | field_category: Category |
|
245 | 245 | field_title: Title |
|
246 | 246 | field_project: Project |
|
247 | 247 | field_issue: Issue |
|
248 | 248 | field_status: Status |
|
249 | 249 | field_notes: Notes |
|
250 | 250 | field_is_closed: Issue closed |
|
251 | 251 | field_is_default: Default value |
|
252 | 252 | field_tracker: Tracker |
|
253 | 253 | field_subject: Subject |
|
254 | 254 | field_due_date: Due date |
|
255 | 255 | field_assigned_to: Assignee |
|
256 | 256 | field_priority: Priority |
|
257 | 257 | field_fixed_version: Target version |
|
258 | 258 | field_user: User |
|
259 | 259 | field_principal: Principal |
|
260 | 260 | field_role: Role |
|
261 | 261 | field_homepage: Homepage |
|
262 | 262 | field_is_public: Public |
|
263 | 263 | field_parent: Subproject of |
|
264 | 264 | field_is_in_roadmap: Issues displayed in roadmap |
|
265 | 265 | field_login: Login |
|
266 | 266 | field_mail_notification: Email notifications |
|
267 | 267 | field_admin: Administrator |
|
268 | 268 | field_last_login_on: Last connection |
|
269 | 269 | field_language: Language |
|
270 | 270 | field_effective_date: Date |
|
271 | 271 | field_password: Password |
|
272 | 272 | field_new_password: New password |
|
273 | 273 | field_password_confirmation: Confirmation |
|
274 | 274 | field_version: Version |
|
275 | 275 | field_type: Type |
|
276 | 276 | field_host: Host |
|
277 | 277 | field_port: Port |
|
278 | 278 | field_account: Account |
|
279 | 279 | field_base_dn: Base DN |
|
280 | 280 | field_attr_login: Login attribute |
|
281 | 281 | field_attr_firstname: Firstname attribute |
|
282 | 282 | field_attr_lastname: Lastname attribute |
|
283 | 283 | field_attr_mail: Email attribute |
|
284 | 284 | field_onthefly: On-the-fly user creation |
|
285 | 285 | field_start_date: Start date |
|
286 | 286 | field_done_ratio: "% Done" |
|
287 | 287 | field_auth_source: Authentication mode |
|
288 | 288 | field_hide_mail: Hide my email address |
|
289 | 289 | field_comments: Comment |
|
290 | 290 | field_url: URL |
|
291 | 291 | field_start_page: Start page |
|
292 | 292 | field_subproject: Subproject |
|
293 | 293 | field_hours: Hours |
|
294 | 294 | field_activity: Activity |
|
295 | 295 | field_spent_on: Date |
|
296 | 296 | field_identifier: Identifier |
|
297 | 297 | field_is_filter: Used as a filter |
|
298 | 298 | field_issue_to: Related issue |
|
299 | 299 | field_delay: Delay |
|
300 | 300 | field_assignable: Issues can be assigned to this role |
|
301 | 301 | field_redirect_existing_links: Redirect existing links |
|
302 | 302 | field_estimated_hours: Estimated time |
|
303 | 303 | field_column_names: Columns |
|
304 | 304 | field_time_entries: Log time |
|
305 | 305 | field_time_zone: Time zone |
|
306 | 306 | field_searchable: Searchable |
|
307 | 307 | field_default_value: Default value |
|
308 | 308 | field_comments_sorting: Display comments |
|
309 | 309 | field_parent_title: Parent page |
|
310 | 310 | field_editable: Editable |
|
311 | 311 | field_watcher: Watcher |
|
312 | 312 | field_identity_url: OpenID URL |
|
313 | 313 | field_content: Content |
|
314 | 314 | field_group_by: Group results by |
|
315 | 315 | field_sharing: Sharing |
|
316 | 316 | field_parent_issue: Parent task |
|
317 | 317 | field_member_of_group: "Assignee's group" |
|
318 | 318 | field_assigned_to_role: "Assignee's role" |
|
319 | 319 | field_text: Text field |
|
320 | 320 | field_visible: Visible |
|
321 | 321 | field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text" |
|
322 | 322 | field_issues_visibility: Issues visibility |
|
323 | 323 | field_is_private: Private |
|
324 | 324 | field_commit_logs_encoding: Commit messages encoding |
|
325 | 325 | field_scm_path_encoding: Path encoding |
|
326 | 326 | field_path_to_repository: Path to repository |
|
327 | 327 | field_root_directory: Root directory |
|
328 | 328 | field_cvsroot: CVSROOT |
|
329 | 329 | field_cvs_module: Module |
|
330 | 330 | field_repository_is_default: Main repository |
|
331 | 331 | field_multiple: Multiple values |
|
332 | 332 | field_auth_source_ldap_filter: LDAP filter |
|
333 | 333 | field_core_fields: Standard fields |
|
334 | 334 | field_timeout: "Timeout (in seconds)" |
|
335 | 335 | field_board_parent: Parent forum |
|
336 | 336 | field_private_notes: Private notes |
|
337 | 337 | field_inherit_members: Inherit members |
|
338 | 338 | field_generate_password: Generate password |
|
339 | 339 | field_must_change_passwd: Must change password at next logon |
|
340 | 340 | |
|
341 | 341 | setting_app_title: Application title |
|
342 | 342 | setting_app_subtitle: Application subtitle |
|
343 | 343 | setting_welcome_text: Welcome text |
|
344 | 344 | setting_default_language: Default language |
|
345 | 345 | setting_login_required: Authentication required |
|
346 | 346 | setting_self_registration: Self-registration |
|
347 | 347 | setting_attachment_max_size: Maximum attachment size |
|
348 | 348 | setting_issues_export_limit: Issues export limit |
|
349 | 349 | setting_mail_from: Emission email address |
|
350 | 350 | setting_bcc_recipients: Blind carbon copy recipients (bcc) |
|
351 | 351 | setting_plain_text_mail: Plain text mail (no HTML) |
|
352 | 352 | setting_host_name: Host name and path |
|
353 | 353 | setting_text_formatting: Text formatting |
|
354 | 354 | setting_wiki_compression: Wiki history compression |
|
355 | 355 | setting_feeds_limit: Maximum number of items in Atom feeds |
|
356 | 356 | setting_default_projects_public: New projects are public by default |
|
357 | 357 | setting_autofetch_changesets: Fetch commits automatically |
|
358 | 358 | setting_sys_api_enabled: Enable WS for repository management |
|
359 | 359 | setting_commit_ref_keywords: Referencing keywords |
|
360 | 360 | setting_commit_fix_keywords: Fixing keywords |
|
361 | 361 | setting_autologin: Autologin |
|
362 | 362 | setting_date_format: Date format |
|
363 | 363 | setting_time_format: Time format |
|
364 | 364 | setting_cross_project_issue_relations: Allow cross-project issue relations |
|
365 | 365 | setting_cross_project_subtasks: Allow cross-project subtasks |
|
366 | 366 | setting_issue_list_default_columns: Default columns displayed on the issue list |
|
367 | 367 | setting_repositories_encodings: Attachments and repositories encodings |
|
368 | 368 | setting_emails_header: Email header |
|
369 | 369 | setting_emails_footer: Email footer |
|
370 | 370 | setting_protocol: Protocol |
|
371 | 371 | setting_per_page_options: Objects per page options |
|
372 | 372 | setting_user_format: Users display format |
|
373 | 373 | setting_activity_days_default: Days displayed on project activity |
|
374 | 374 | setting_display_subprojects_issues: Display subprojects issues on main projects by default |
|
375 | 375 | setting_enabled_scm: Enabled SCM |
|
376 | 376 | setting_mail_handler_body_delimiters: "Truncate emails after one of these lines" |
|
377 | 377 | setting_mail_handler_api_enabled: Enable WS for incoming emails |
|
378 | 378 | setting_mail_handler_api_key: API key |
|
379 | 379 | setting_sequential_project_identifiers: Generate sequential project identifiers |
|
380 | 380 | setting_gravatar_enabled: Use Gravatar user icons |
|
381 | 381 | setting_gravatar_default: Default Gravatar image |
|
382 | 382 | setting_diff_max_lines_displayed: Maximum number of diff lines displayed |
|
383 | 383 | setting_file_max_size_displayed: Maximum size of text files displayed inline |
|
384 | 384 | setting_repository_log_display_limit: Maximum number of revisions displayed on file log |
|
385 | 385 | setting_openid: Allow OpenID login and registration |
|
386 | 386 | setting_password_min_length: Minimum password length |
|
387 | 387 | setting_new_project_user_role_id: Role given to a non-admin user who creates a project |
|
388 | 388 | setting_default_projects_modules: Default enabled modules for new projects |
|
389 | 389 | setting_issue_done_ratio: Calculate the issue done ratio with |
|
390 | 390 | setting_issue_done_ratio_issue_field: Use the issue field |
|
391 | 391 | setting_issue_done_ratio_issue_status: Use the issue status |
|
392 | 392 | setting_start_of_week: Start calendars on |
|
393 | 393 | setting_rest_api_enabled: Enable REST web service |
|
394 | 394 | setting_cache_formatted_text: Cache formatted text |
|
395 | 395 | setting_default_notification_option: Default notification option |
|
396 | 396 | setting_commit_logtime_enabled: Enable time logging |
|
397 | 397 | setting_commit_logtime_activity_id: Activity for logged time |
|
398 | 398 | setting_gantt_items_limit: Maximum number of items displayed on the gantt chart |
|
399 | 399 | setting_issue_group_assignment: Allow issue assignment to groups |
|
400 | 400 | setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues |
|
401 | 401 | setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed |
|
402 | 402 | setting_unsubscribe: Allow users to delete their own account |
|
403 | 403 | setting_session_lifetime: Session maximum lifetime |
|
404 | 404 | setting_session_timeout: Session inactivity timeout |
|
405 | 405 | setting_thumbnails_enabled: Display attachment thumbnails |
|
406 | 406 | setting_thumbnails_size: Thumbnails size (in pixels) |
|
407 | 407 | setting_non_working_week_days: Non-working days |
|
408 | 408 | setting_jsonp_enabled: Enable JSONP support |
|
409 | 409 | setting_default_projects_tracker_ids: Default trackers for new projects |
|
410 | setting_mail_handler_excluded_filenames: Exclude attachments by name | |
|
410 | 411 | |
|
411 | 412 | permission_add_project: Create project |
|
412 | 413 | permission_add_subprojects: Create subprojects |
|
413 | 414 | permission_edit_project: Edit project |
|
414 | 415 | permission_close_project: Close / reopen the project |
|
415 | 416 | permission_select_project_modules: Select project modules |
|
416 | 417 | permission_manage_members: Manage members |
|
417 | 418 | permission_manage_project_activities: Manage project activities |
|
418 | 419 | permission_manage_versions: Manage versions |
|
419 | 420 | permission_manage_categories: Manage issue categories |
|
420 | 421 | permission_view_issues: View Issues |
|
421 | 422 | permission_add_issues: Add issues |
|
422 | 423 | permission_edit_issues: Edit issues |
|
423 | 424 | permission_manage_issue_relations: Manage issue relations |
|
424 | 425 | permission_set_issues_private: Set issues public or private |
|
425 | 426 | permission_set_own_issues_private: Set own issues public or private |
|
426 | 427 | permission_add_issue_notes: Add notes |
|
427 | 428 | permission_edit_issue_notes: Edit notes |
|
428 | 429 | permission_edit_own_issue_notes: Edit own notes |
|
429 | 430 | permission_view_private_notes: View private notes |
|
430 | 431 | permission_set_notes_private: Set notes as private |
|
431 | 432 | permission_move_issues: Move issues |
|
432 | 433 | permission_delete_issues: Delete issues |
|
433 | 434 | permission_manage_public_queries: Manage public queries |
|
434 | 435 | permission_save_queries: Save queries |
|
435 | 436 | permission_view_gantt: View gantt chart |
|
436 | 437 | permission_view_calendar: View calendar |
|
437 | 438 | permission_view_issue_watchers: View watchers list |
|
438 | 439 | permission_add_issue_watchers: Add watchers |
|
439 | 440 | permission_delete_issue_watchers: Delete watchers |
|
440 | 441 | permission_log_time: Log spent time |
|
441 | 442 | permission_view_time_entries: View spent time |
|
442 | 443 | permission_edit_time_entries: Edit time logs |
|
443 | 444 | permission_edit_own_time_entries: Edit own time logs |
|
444 | 445 | permission_manage_news: Manage news |
|
445 | 446 | permission_comment_news: Comment news |
|
446 | 447 | permission_view_documents: View documents |
|
447 | 448 | permission_add_documents: Add documents |
|
448 | 449 | permission_edit_documents: Edit documents |
|
449 | 450 | permission_delete_documents: Delete documents |
|
450 | 451 | permission_manage_files: Manage files |
|
451 | 452 | permission_view_files: View files |
|
452 | 453 | permission_manage_wiki: Manage wiki |
|
453 | 454 | permission_rename_wiki_pages: Rename wiki pages |
|
454 | 455 | permission_delete_wiki_pages: Delete wiki pages |
|
455 | 456 | permission_view_wiki_pages: View wiki |
|
456 | 457 | permission_view_wiki_edits: View wiki history |
|
457 | 458 | permission_edit_wiki_pages: Edit wiki pages |
|
458 | 459 | permission_delete_wiki_pages_attachments: Delete attachments |
|
459 | 460 | permission_protect_wiki_pages: Protect wiki pages |
|
460 | 461 | permission_manage_repository: Manage repository |
|
461 | 462 | permission_browse_repository: Browse repository |
|
462 | 463 | permission_view_changesets: View changesets |
|
463 | 464 | permission_commit_access: Commit access |
|
464 | 465 | permission_manage_boards: Manage forums |
|
465 | 466 | permission_view_messages: View messages |
|
466 | 467 | permission_add_messages: Post messages |
|
467 | 468 | permission_edit_messages: Edit messages |
|
468 | 469 | permission_edit_own_messages: Edit own messages |
|
469 | 470 | permission_delete_messages: Delete messages |
|
470 | 471 | permission_delete_own_messages: Delete own messages |
|
471 | 472 | permission_export_wiki_pages: Export wiki pages |
|
472 | 473 | permission_manage_subtasks: Manage subtasks |
|
473 | 474 | permission_manage_related_issues: Manage related issues |
|
474 | 475 | |
|
475 | 476 | project_module_issue_tracking: Issue tracking |
|
476 | 477 | project_module_time_tracking: Time tracking |
|
477 | 478 | project_module_news: News |
|
478 | 479 | project_module_documents: Documents |
|
479 | 480 | project_module_files: Files |
|
480 | 481 | project_module_wiki: Wiki |
|
481 | 482 | project_module_repository: Repository |
|
482 | 483 | project_module_boards: Forums |
|
483 | 484 | project_module_calendar: Calendar |
|
484 | 485 | project_module_gantt: Gantt |
|
485 | 486 | |
|
486 | 487 | label_user: User |
|
487 | 488 | label_user_plural: Users |
|
488 | 489 | label_user_new: New user |
|
489 | 490 | label_user_anonymous: Anonymous |
|
490 | 491 | label_project: Project |
|
491 | 492 | label_project_new: New project |
|
492 | 493 | label_project_plural: Projects |
|
493 | 494 | label_x_projects: |
|
494 | 495 | zero: no projects |
|
495 | 496 | one: 1 project |
|
496 | 497 | other: "%{count} projects" |
|
497 | 498 | label_project_all: All Projects |
|
498 | 499 | label_project_latest: Latest projects |
|
499 | 500 | label_issue: Issue |
|
500 | 501 | label_issue_new: New issue |
|
501 | 502 | label_issue_plural: Issues |
|
502 | 503 | label_issue_view_all: View all issues |
|
503 | 504 | label_issues_by: "Issues by %{value}" |
|
504 | 505 | label_issue_added: Issue added |
|
505 | 506 | label_issue_updated: Issue updated |
|
506 | 507 | label_issue_note_added: Note added |
|
507 | 508 | label_issue_status_updated: Status updated |
|
508 | 509 | label_issue_priority_updated: Priority updated |
|
509 | 510 | label_document: Document |
|
510 | 511 | label_document_new: New document |
|
511 | 512 | label_document_plural: Documents |
|
512 | 513 | label_document_added: Document added |
|
513 | 514 | label_role: Role |
|
514 | 515 | label_role_plural: Roles |
|
515 | 516 | label_role_new: New role |
|
516 | 517 | label_role_and_permissions: Roles and permissions |
|
517 | 518 | label_role_anonymous: Anonymous |
|
518 | 519 | label_role_non_member: Non member |
|
519 | 520 | label_member: Member |
|
520 | 521 | label_member_new: New member |
|
521 | 522 | label_member_plural: Members |
|
522 | 523 | label_tracker: Tracker |
|
523 | 524 | label_tracker_plural: Trackers |
|
524 | 525 | label_tracker_new: New tracker |
|
525 | 526 | label_workflow: Workflow |
|
526 | 527 | label_issue_status: Issue status |
|
527 | 528 | label_issue_status_plural: Issue statuses |
|
528 | 529 | label_issue_status_new: New status |
|
529 | 530 | label_issue_category: Issue category |
|
530 | 531 | label_issue_category_plural: Issue categories |
|
531 | 532 | label_issue_category_new: New category |
|
532 | 533 | label_custom_field: Custom field |
|
533 | 534 | label_custom_field_plural: Custom fields |
|
534 | 535 | label_custom_field_new: New custom field |
|
535 | 536 | label_enumerations: Enumerations |
|
536 | 537 | label_enumeration_new: New value |
|
537 | 538 | label_information: Information |
|
538 | 539 | label_information_plural: Information |
|
539 | 540 | label_please_login: Please log in |
|
540 | 541 | label_register: Register |
|
541 | 542 | label_login_with_open_id_option: or login with OpenID |
|
542 | 543 | label_password_lost: Lost password |
|
543 | 544 | label_home: Home |
|
544 | 545 | label_my_page: My page |
|
545 | 546 | label_my_account: My account |
|
546 | 547 | label_my_projects: My projects |
|
547 | 548 | label_my_page_block: My page block |
|
548 | 549 | label_administration: Administration |
|
549 | 550 | label_login: Sign in |
|
550 | 551 | label_logout: Sign out |
|
551 | 552 | label_help: Help |
|
552 | 553 | label_reported_issues: Reported issues |
|
553 | 554 | label_assigned_to_me_issues: Issues assigned to me |
|
554 | 555 | label_last_login: Last connection |
|
555 | 556 | label_registered_on: Registered on |
|
556 | 557 | label_activity: Activity |
|
557 | 558 | label_overall_activity: Overall activity |
|
558 | 559 | label_user_activity: "%{value}'s activity" |
|
559 | 560 | label_new: New |
|
560 | 561 | label_logged_as: Logged in as |
|
561 | 562 | label_environment: Environment |
|
562 | 563 | label_authentication: Authentication |
|
563 | 564 | label_auth_source: Authentication mode |
|
564 | 565 | label_auth_source_new: New authentication mode |
|
565 | 566 | label_auth_source_plural: Authentication modes |
|
566 | 567 | label_subproject_plural: Subprojects |
|
567 | 568 | label_subproject_new: New subproject |
|
568 | 569 | label_and_its_subprojects: "%{value} and its subprojects" |
|
569 | 570 | label_min_max_length: Min - Max length |
|
570 | 571 | label_list: List |
|
571 | 572 | label_date: Date |
|
572 | 573 | label_integer: Integer |
|
573 | 574 | label_float: Float |
|
574 | 575 | label_boolean: Boolean |
|
575 | 576 | label_string: Text |
|
576 | 577 | label_text: Long text |
|
577 | 578 | label_attribute: Attribute |
|
578 | 579 | label_attribute_plural: Attributes |
|
579 | 580 | label_no_data: No data to display |
|
580 | 581 | label_change_status: Change status |
|
581 | 582 | label_history: History |
|
582 | 583 | label_attachment: File |
|
583 | 584 | label_attachment_new: New file |
|
584 | 585 | label_attachment_delete: Delete file |
|
585 | 586 | label_attachment_plural: Files |
|
586 | 587 | label_file_added: File added |
|
587 | 588 | label_report: Report |
|
588 | 589 | label_report_plural: Reports |
|
589 | 590 | label_news: News |
|
590 | 591 | label_news_new: Add news |
|
591 | 592 | label_news_plural: News |
|
592 | 593 | label_news_latest: Latest news |
|
593 | 594 | label_news_view_all: View all news |
|
594 | 595 | label_news_added: News added |
|
595 | 596 | label_news_comment_added: Comment added to a news |
|
596 | 597 | label_settings: Settings |
|
597 | 598 | label_overview: Overview |
|
598 | 599 | label_version: Version |
|
599 | 600 | label_version_new: New version |
|
600 | 601 | label_version_plural: Versions |
|
601 | 602 | label_close_versions: Close completed versions |
|
602 | 603 | label_confirmation: Confirmation |
|
603 | 604 | label_export_to: 'Also available in:' |
|
604 | 605 | label_read: Read... |
|
605 | 606 | label_public_projects: Public projects |
|
606 | 607 | label_open_issues: open |
|
607 | 608 | label_open_issues_plural: open |
|
608 | 609 | label_closed_issues: closed |
|
609 | 610 | label_closed_issues_plural: closed |
|
610 | 611 | label_x_open_issues_abbr_on_total: |
|
611 | 612 | zero: 0 open / %{total} |
|
612 | 613 | one: 1 open / %{total} |
|
613 | 614 | other: "%{count} open / %{total}" |
|
614 | 615 | label_x_open_issues_abbr: |
|
615 | 616 | zero: 0 open |
|
616 | 617 | one: 1 open |
|
617 | 618 | other: "%{count} open" |
|
618 | 619 | label_x_closed_issues_abbr: |
|
619 | 620 | zero: 0 closed |
|
620 | 621 | one: 1 closed |
|
621 | 622 | other: "%{count} closed" |
|
622 | 623 | label_x_issues: |
|
623 | 624 | zero: 0 issues |
|
624 | 625 | one: 1 issue |
|
625 | 626 | other: "%{count} issues" |
|
626 | 627 | label_total: Total |
|
627 | 628 | label_total_time: Total time |
|
628 | 629 | label_permissions: Permissions |
|
629 | 630 | label_current_status: Current status |
|
630 | 631 | label_new_statuses_allowed: New statuses allowed |
|
631 | 632 | label_all: all |
|
632 | 633 | label_any: any |
|
633 | 634 | label_none: none |
|
634 | 635 | label_nobody: nobody |
|
635 | 636 | label_next: Next |
|
636 | 637 | label_previous: Previous |
|
637 | 638 | label_used_by: Used by |
|
638 | 639 | label_details: Details |
|
639 | 640 | label_add_note: Add a note |
|
640 | 641 | label_per_page: Per page |
|
641 | 642 | label_calendar: Calendar |
|
642 | 643 | label_months_from: months from |
|
643 | 644 | label_gantt: Gantt |
|
644 | 645 | label_internal: Internal |
|
645 | 646 | label_last_changes: "last %{count} changes" |
|
646 | 647 | label_change_view_all: View all changes |
|
647 | 648 | label_personalize_page: Personalize this page |
|
648 | 649 | label_comment: Comment |
|
649 | 650 | label_comment_plural: Comments |
|
650 | 651 | label_x_comments: |
|
651 | 652 | zero: no comments |
|
652 | 653 | one: 1 comment |
|
653 | 654 | other: "%{count} comments" |
|
654 | 655 | label_comment_add: Add a comment |
|
655 | 656 | label_comment_added: Comment added |
|
656 | 657 | label_comment_delete: Delete comments |
|
657 | 658 | label_query: Custom query |
|
658 | 659 | label_query_plural: Custom queries |
|
659 | 660 | label_query_new: New query |
|
660 | 661 | label_my_queries: My custom queries |
|
661 | 662 | label_filter_add: Add filter |
|
662 | 663 | label_filter_plural: Filters |
|
663 | 664 | label_equals: is |
|
664 | 665 | label_not_equals: is not |
|
665 | 666 | label_in_less_than: in less than |
|
666 | 667 | label_in_more_than: in more than |
|
667 | 668 | label_in_the_next_days: in the next |
|
668 | 669 | label_in_the_past_days: in the past |
|
669 | 670 | label_greater_or_equal: '>=' |
|
670 | 671 | label_less_or_equal: '<=' |
|
671 | 672 | label_between: between |
|
672 | 673 | label_in: in |
|
673 | 674 | label_today: today |
|
674 | 675 | label_all_time: all time |
|
675 | 676 | label_yesterday: yesterday |
|
676 | 677 | label_this_week: this week |
|
677 | 678 | label_last_week: last week |
|
678 | 679 | label_last_n_weeks: "last %{count} weeks" |
|
679 | 680 | label_last_n_days: "last %{count} days" |
|
680 | 681 | label_this_month: this month |
|
681 | 682 | label_last_month: last month |
|
682 | 683 | label_this_year: this year |
|
683 | 684 | label_date_range: Date range |
|
684 | 685 | label_less_than_ago: less than days ago |
|
685 | 686 | label_more_than_ago: more than days ago |
|
686 | 687 | label_ago: days ago |
|
687 | 688 | label_contains: contains |
|
688 | 689 | label_not_contains: doesn't contain |
|
689 | 690 | label_any_issues_in_project: any issues in project |
|
690 | 691 | label_any_issues_not_in_project: any issues not in project |
|
691 | 692 | label_no_issues_in_project: no issues in project |
|
692 | 693 | label_day_plural: days |
|
693 | 694 | label_repository: Repository |
|
694 | 695 | label_repository_new: New repository |
|
695 | 696 | label_repository_plural: Repositories |
|
696 | 697 | label_browse: Browse |
|
697 | 698 | label_branch: Branch |
|
698 | 699 | label_tag: Tag |
|
699 | 700 | label_revision: Revision |
|
700 | 701 | label_revision_plural: Revisions |
|
701 | 702 | label_revision_id: "Revision %{value}" |
|
702 | 703 | label_associated_revisions: Associated revisions |
|
703 | 704 | label_added: added |
|
704 | 705 | label_modified: modified |
|
705 | 706 | label_copied: copied |
|
706 | 707 | label_renamed: renamed |
|
707 | 708 | label_deleted: deleted |
|
708 | 709 | label_latest_revision: Latest revision |
|
709 | 710 | label_latest_revision_plural: Latest revisions |
|
710 | 711 | label_view_revisions: View revisions |
|
711 | 712 | label_view_all_revisions: View all revisions |
|
712 | 713 | label_max_size: Maximum size |
|
713 | 714 | label_sort_highest: Move to top |
|
714 | 715 | label_sort_higher: Move up |
|
715 | 716 | label_sort_lower: Move down |
|
716 | 717 | label_sort_lowest: Move to bottom |
|
717 | 718 | label_roadmap: Roadmap |
|
718 | 719 | label_roadmap_due_in: "Due in %{value}" |
|
719 | 720 | label_roadmap_overdue: "%{value} late" |
|
720 | 721 | label_roadmap_no_issues: No issues for this version |
|
721 | 722 | label_search: Search |
|
722 | 723 | label_result_plural: Results |
|
723 | 724 | label_all_words: All words |
|
724 | 725 | label_wiki: Wiki |
|
725 | 726 | label_wiki_edit: Wiki edit |
|
726 | 727 | label_wiki_edit_plural: Wiki edits |
|
727 | 728 | label_wiki_page: Wiki page |
|
728 | 729 | label_wiki_page_plural: Wiki pages |
|
729 | 730 | label_index_by_title: Index by title |
|
730 | 731 | label_index_by_date: Index by date |
|
731 | 732 | label_current_version: Current version |
|
732 | 733 | label_preview: Preview |
|
733 | 734 | label_feed_plural: Feeds |
|
734 | 735 | label_changes_details: Details of all changes |
|
735 | 736 | label_issue_tracking: Issue tracking |
|
736 | 737 | label_spent_time: Spent time |
|
737 | 738 | label_overall_spent_time: Overall spent time |
|
738 | 739 | label_f_hour: "%{value} hour" |
|
739 | 740 | label_f_hour_plural: "%{value} hours" |
|
740 | 741 | label_time_tracking: Time tracking |
|
741 | 742 | label_change_plural: Changes |
|
742 | 743 | label_statistics: Statistics |
|
743 | 744 | label_commits_per_month: Commits per month |
|
744 | 745 | label_commits_per_author: Commits per author |
|
745 | 746 | label_diff: diff |
|
746 | 747 | label_view_diff: View differences |
|
747 | 748 | label_diff_inline: inline |
|
748 | 749 | label_diff_side_by_side: side by side |
|
749 | 750 | label_options: Options |
|
750 | 751 | label_copy_workflow_from: Copy workflow from |
|
751 | 752 | label_permissions_report: Permissions report |
|
752 | 753 | label_watched_issues: Watched issues |
|
753 | 754 | label_related_issues: Related issues |
|
754 | 755 | label_applied_status: Applied status |
|
755 | 756 | label_loading: Loading... |
|
756 | 757 | label_relation_new: New relation |
|
757 | 758 | label_relation_delete: Delete relation |
|
758 | 759 | label_relates_to: Related to |
|
759 | 760 | label_duplicates: Duplicates |
|
760 | 761 | label_duplicated_by: Duplicated by |
|
761 | 762 | label_blocks: Blocks |
|
762 | 763 | label_blocked_by: Blocked by |
|
763 | 764 | label_precedes: Precedes |
|
764 | 765 | label_follows: Follows |
|
765 | 766 | label_copied_to: Copied to |
|
766 | 767 | label_copied_from: Copied from |
|
767 | 768 | label_end_to_start: end to start |
|
768 | 769 | label_end_to_end: end to end |
|
769 | 770 | label_start_to_start: start to start |
|
770 | 771 | label_start_to_end: start to end |
|
771 | 772 | label_stay_logged_in: Stay logged in |
|
772 | 773 | label_disabled: disabled |
|
773 | 774 | label_show_completed_versions: Show completed versions |
|
774 | 775 | label_me: me |
|
775 | 776 | label_board: Forum |
|
776 | 777 | label_board_new: New forum |
|
777 | 778 | label_board_plural: Forums |
|
778 | 779 | label_board_locked: Locked |
|
779 | 780 | label_board_sticky: Sticky |
|
780 | 781 | label_topic_plural: Topics |
|
781 | 782 | label_message_plural: Messages |
|
782 | 783 | label_message_last: Last message |
|
783 | 784 | label_message_new: New message |
|
784 | 785 | label_message_posted: Message added |
|
785 | 786 | label_reply_plural: Replies |
|
786 | 787 | label_send_information: Send account information to the user |
|
787 | 788 | label_year: Year |
|
788 | 789 | label_month: Month |
|
789 | 790 | label_week: Week |
|
790 | 791 | label_date_from: From |
|
791 | 792 | label_date_to: To |
|
792 | 793 | label_language_based: Based on user's language |
|
793 | 794 | label_sort_by: "Sort by %{value}" |
|
794 | 795 | label_send_test_email: Send a test email |
|
795 | 796 | label_feeds_access_key: Atom access key |
|
796 | 797 | label_missing_feeds_access_key: Missing a Atom access key |
|
797 | 798 | label_feeds_access_key_created_on: "Atom access key created %{value} ago" |
|
798 | 799 | label_module_plural: Modules |
|
799 | 800 | label_added_time_by: "Added by %{author} %{age} ago" |
|
800 | 801 | label_updated_time_by: "Updated by %{author} %{age} ago" |
|
801 | 802 | label_updated_time: "Updated %{value} ago" |
|
802 | 803 | label_jump_to_a_project: Jump to a project... |
|
803 | 804 | label_file_plural: Files |
|
804 | 805 | label_changeset_plural: Changesets |
|
805 | 806 | label_default_columns: Default columns |
|
806 | 807 | label_no_change_option: (No change) |
|
807 | 808 | label_bulk_edit_selected_issues: Bulk edit selected issues |
|
808 | 809 | label_bulk_edit_selected_time_entries: Bulk edit selected time entries |
|
809 | 810 | label_theme: Theme |
|
810 | 811 | label_default: Default |
|
811 | 812 | label_search_titles_only: Search titles only |
|
812 | 813 | label_user_mail_option_all: "For any event on all my projects" |
|
813 | 814 | label_user_mail_option_selected: "For any event on the selected projects only..." |
|
814 | 815 | label_user_mail_option_none: "No events" |
|
815 | 816 | label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" |
|
816 | 817 | label_user_mail_option_only_assigned: "Only for things I am assigned to" |
|
817 | 818 | label_user_mail_option_only_owner: "Only for things I am the owner of" |
|
818 | 819 | label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself" |
|
819 | 820 | label_registration_activation_by_email: account activation by email |
|
820 | 821 | label_registration_manual_activation: manual account activation |
|
821 | 822 | label_registration_automatic_activation: automatic account activation |
|
822 | 823 | label_display_per_page: "Per page: %{value}" |
|
823 | 824 | label_age: Age |
|
824 | 825 | label_change_properties: Change properties |
|
825 | 826 | label_general: General |
|
826 | 827 | label_more: More |
|
827 | 828 | label_scm: SCM |
|
828 | 829 | label_plugins: Plugins |
|
829 | 830 | label_ldap_authentication: LDAP authentication |
|
830 | 831 | label_downloads_abbr: D/L |
|
831 | 832 | label_optional_description: Optional description |
|
832 | 833 | label_add_another_file: Add another file |
|
833 | 834 | label_preferences: Preferences |
|
834 | 835 | label_chronological_order: In chronological order |
|
835 | 836 | label_reverse_chronological_order: In reverse chronological order |
|
836 | 837 | label_planning: Planning |
|
837 | 838 | label_incoming_emails: Incoming emails |
|
838 | 839 | label_generate_key: Generate a key |
|
839 | 840 | label_issue_watchers: Watchers |
|
840 | 841 | label_example: Example |
|
841 | 842 | label_display: Display |
|
842 | 843 | label_sort: Sort |
|
843 | 844 | label_ascending: Ascending |
|
844 | 845 | label_descending: Descending |
|
845 | 846 | label_date_from_to: From %{start} to %{end} |
|
846 | 847 | label_wiki_content_added: Wiki page added |
|
847 | 848 | label_wiki_content_updated: Wiki page updated |
|
848 | 849 | label_group: Group |
|
849 | 850 | label_group_plural: Groups |
|
850 | 851 | label_group_new: New group |
|
851 | 852 | label_time_entry_plural: Spent time |
|
852 | 853 | label_version_sharing_none: Not shared |
|
853 | 854 | label_version_sharing_descendants: With subprojects |
|
854 | 855 | label_version_sharing_hierarchy: With project hierarchy |
|
855 | 856 | label_version_sharing_tree: With project tree |
|
856 | 857 | label_version_sharing_system: With all projects |
|
857 | 858 | label_update_issue_done_ratios: Update issue done ratios |
|
858 | 859 | label_copy_source: Source |
|
859 | 860 | label_copy_target: Target |
|
860 | 861 | label_copy_same_as_target: Same as target |
|
861 | 862 | label_display_used_statuses_only: Only display statuses that are used by this tracker |
|
862 | 863 | label_api_access_key: API access key |
|
863 | 864 | label_missing_api_access_key: Missing an API access key |
|
864 | 865 | label_api_access_key_created_on: "API access key created %{value} ago" |
|
865 | 866 | label_profile: Profile |
|
866 | 867 | label_subtask_plural: Subtasks |
|
867 | 868 | label_project_copy_notifications: Send email notifications during the project copy |
|
868 | 869 | label_principal_search: "Search for user or group:" |
|
869 | 870 | label_user_search: "Search for user:" |
|
870 | 871 | label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author |
|
871 | 872 | label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee |
|
872 | 873 | label_issues_visibility_all: All issues |
|
873 | 874 | label_issues_visibility_public: All non private issues |
|
874 | 875 | label_issues_visibility_own: Issues created by or assigned to the user |
|
875 | 876 | label_git_report_last_commit: Report last commit for files and directories |
|
876 | 877 | label_parent_revision: Parent |
|
877 | 878 | label_child_revision: Child |
|
878 | 879 | label_export_options: "%{export_format} export options" |
|
879 | 880 | label_copy_attachments: Copy attachments |
|
880 | 881 | label_copy_subtasks: Copy subtasks |
|
881 | 882 | label_item_position: "%{position} of %{count}" |
|
882 | 883 | label_completed_versions: Completed versions |
|
883 | 884 | label_search_for_watchers: Search for watchers to add |
|
884 | 885 | label_session_expiration: Session expiration |
|
885 | 886 | label_show_closed_projects: View closed projects |
|
886 | 887 | label_status_transitions: Status transitions |
|
887 | 888 | label_fields_permissions: Fields permissions |
|
888 | 889 | label_readonly: Read-only |
|
889 | 890 | label_required: Required |
|
890 | 891 | label_hidden: Hidden |
|
891 | 892 | label_attribute_of_project: "Project's %{name}" |
|
892 | 893 | label_attribute_of_issue: "Issue's %{name}" |
|
893 | 894 | label_attribute_of_author: "Author's %{name}" |
|
894 | 895 | label_attribute_of_assigned_to: "Assignee's %{name}" |
|
895 | 896 | label_attribute_of_user: "User's %{name}" |
|
896 | 897 | label_attribute_of_fixed_version: "Target version's %{name}" |
|
897 | 898 | label_cross_project_descendants: With subprojects |
|
898 | 899 | label_cross_project_tree: With project tree |
|
899 | 900 | label_cross_project_hierarchy: With project hierarchy |
|
900 | 901 | label_cross_project_system: With all projects |
|
901 | 902 | label_gantt_progress_line: Progress line |
|
902 | 903 | label_visibility_private: to me only |
|
903 | 904 | label_visibility_roles: to these roles only |
|
904 | 905 | label_visibility_public: to any users |
|
905 | 906 | |
|
906 | 907 | button_login: Login |
|
907 | 908 | button_submit: Submit |
|
908 | 909 | button_save: Save |
|
909 | 910 | button_check_all: Check all |
|
910 | 911 | button_uncheck_all: Uncheck all |
|
911 | 912 | button_collapse_all: Collapse all |
|
912 | 913 | button_expand_all: Expand all |
|
913 | 914 | button_delete: Delete |
|
914 | 915 | button_create: Create |
|
915 | 916 | button_create_and_continue: Create and continue |
|
916 | 917 | button_test: Test |
|
917 | 918 | button_edit: Edit |
|
918 | 919 | button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" |
|
919 | 920 | button_add: Add |
|
920 | 921 | button_change: Change |
|
921 | 922 | button_apply: Apply |
|
922 | 923 | button_clear: Clear |
|
923 | 924 | button_lock: Lock |
|
924 | 925 | button_unlock: Unlock |
|
925 | 926 | button_download: Download |
|
926 | 927 | button_list: List |
|
927 | 928 | button_view: View |
|
928 | 929 | button_move: Move |
|
929 | 930 | button_move_and_follow: Move and follow |
|
930 | 931 | button_back: Back |
|
931 | 932 | button_cancel: Cancel |
|
932 | 933 | button_activate: Activate |
|
933 | 934 | button_sort: Sort |
|
934 | 935 | button_log_time: Log time |
|
935 | 936 | button_rollback: Rollback to this version |
|
936 | 937 | button_watch: Watch |
|
937 | 938 | button_unwatch: Unwatch |
|
938 | 939 | button_reply: Reply |
|
939 | 940 | button_archive: Archive |
|
940 | 941 | button_unarchive: Unarchive |
|
941 | 942 | button_reset: Reset |
|
942 | 943 | button_rename: Rename |
|
943 | 944 | button_change_password: Change password |
|
944 | 945 | button_copy: Copy |
|
945 | 946 | button_copy_and_follow: Copy and follow |
|
946 | 947 | button_annotate: Annotate |
|
947 | 948 | button_update: Update |
|
948 | 949 | button_configure: Configure |
|
949 | 950 | button_quote: Quote |
|
950 | 951 | button_duplicate: Duplicate |
|
951 | 952 | button_show: Show |
|
952 | 953 | button_hide: Hide |
|
953 | 954 | button_edit_section: Edit this section |
|
954 | 955 | button_export: Export |
|
955 | 956 | button_delete_my_account: Delete my account |
|
956 | 957 | button_close: Close |
|
957 | 958 | button_reopen: Reopen |
|
958 | 959 | |
|
959 | 960 | status_active: active |
|
960 | 961 | status_registered: registered |
|
961 | 962 | status_locked: locked |
|
962 | 963 | |
|
963 | 964 | project_status_active: active |
|
964 | 965 | project_status_closed: closed |
|
965 | 966 | project_status_archived: archived |
|
966 | 967 | |
|
967 | 968 | version_status_open: open |
|
968 | 969 | version_status_locked: locked |
|
969 | 970 | version_status_closed: closed |
|
970 | 971 | |
|
971 | 972 | field_active: Active |
|
972 | 973 | |
|
973 | 974 | text_select_mail_notifications: Select actions for which email notifications should be sent. |
|
974 | 975 | text_regexp_info: eg. ^[A-Z0-9]+$ |
|
975 | 976 | text_min_max_length_info: 0 means no restriction |
|
976 | 977 | text_project_destroy_confirmation: Are you sure you want to delete this project and related data? |
|
977 | 978 | text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted." |
|
978 | 979 | text_workflow_edit: Select a role and a tracker to edit the workflow |
|
979 | 980 | text_are_you_sure: Are you sure? |
|
980 | 981 | text_journal_changed: "%{label} changed from %{old} to %{new}" |
|
981 | 982 | text_journal_changed_no_detail: "%{label} updated" |
|
982 | 983 | text_journal_set_to: "%{label} set to %{value}" |
|
983 | 984 | text_journal_deleted: "%{label} deleted (%{old})" |
|
984 | 985 | text_journal_added: "%{label} %{value} added" |
|
985 | 986 | text_tip_issue_begin_day: issue beginning this day |
|
986 | 987 | text_tip_issue_end_day: issue ending this day |
|
987 | 988 | text_tip_issue_begin_end_day: issue beginning and ending this day |
|
988 | 989 | 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.' |
|
989 | 990 | text_caracters_maximum: "%{count} characters maximum." |
|
990 | 991 | text_caracters_minimum: "Must be at least %{count} characters long." |
|
991 | 992 | text_length_between: "Length between %{min} and %{max} characters." |
|
992 | 993 | text_tracker_no_workflow: No workflow defined for this tracker |
|
993 | 994 | text_unallowed_characters: Unallowed characters |
|
994 | 995 | text_comma_separated: Multiple values allowed (comma separated). |
|
995 | 996 | text_line_separated: Multiple values allowed (one line for each value). |
|
996 | 997 | text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages |
|
997 | 998 | text_issue_added: "Issue %{id} has been reported by %{author}." |
|
998 | 999 | text_issue_updated: "Issue %{id} has been updated by %{author}." |
|
999 | 1000 | text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content? |
|
1000 | 1001 | text_issue_category_destroy_question: "Some issues (%{count}) are assigned to this category. What do you want to do?" |
|
1001 | 1002 | text_issue_category_destroy_assignments: Remove category assignments |
|
1002 | 1003 | text_issue_category_reassign_to: Reassign issues to this category |
|
1003 | 1004 | 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)." |
|
1004 | 1005 | 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." |
|
1005 | 1006 | text_load_default_configuration: Load the default configuration |
|
1006 | 1007 | text_status_changed_by_changeset: "Applied in changeset %{value}." |
|
1007 | 1008 | text_time_logged_by_changeset: "Applied in changeset %{value}." |
|
1008 | 1009 | text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s)?' |
|
1009 | 1010 | text_issues_destroy_descendants_confirmation: "This will also delete %{count} subtask(s)." |
|
1010 | 1011 | text_time_entries_destroy_confirmation: 'Are you sure you want to delete the selected time entr(y/ies)?' |
|
1011 | 1012 | text_select_project_modules: 'Select modules to enable for this project:' |
|
1012 | 1013 | text_default_administrator_account_changed: Default administrator account changed |
|
1013 | 1014 | text_file_repository_writable: Attachments directory writable |
|
1014 | 1015 | text_plugin_assets_writable: Plugin assets directory writable |
|
1015 | 1016 | text_rmagick_available: RMagick available (optional) |
|
1016 | 1017 | text_destroy_time_entries_question: "%{hours} hours were reported on the issues you are about to delete. What do you want to do?" |
|
1017 | 1018 | text_destroy_time_entries: Delete reported hours |
|
1018 | 1019 | text_assign_time_entries_to_project: Assign reported hours to the project |
|
1019 | 1020 | text_reassign_time_entries: 'Reassign reported hours to this issue:' |
|
1020 | 1021 | text_user_wrote: "%{value} wrote:" |
|
1021 | 1022 | text_enumeration_destroy_question: "%{count} objects are assigned to this value." |
|
1022 | 1023 | text_enumeration_category_reassign_to: 'Reassign them to this value:' |
|
1023 | 1024 | 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." |
|
1024 | 1025 | 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." |
|
1025 | 1026 | text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.' |
|
1026 | 1027 | text_custom_field_possible_values_info: 'One line for each value' |
|
1027 | 1028 | text_wiki_page_destroy_question: "This page has %{descendants} child page(s) and descendant(s). What do you want to do?" |
|
1028 | 1029 | text_wiki_page_nullify_children: "Keep child pages as root pages" |
|
1029 | 1030 | text_wiki_page_destroy_children: "Delete child pages and all their descendants" |
|
1030 | 1031 | text_wiki_page_reassign_children: "Reassign child pages to this parent page" |
|
1031 | 1032 | 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?" |
|
1032 | 1033 | text_zoom_in: Zoom in |
|
1033 | 1034 | text_zoom_out: Zoom out |
|
1034 | 1035 | text_warn_on_leaving_unsaved: "The current page contains unsaved text that will be lost if you leave this page." |
|
1035 | 1036 | text_scm_path_encoding_note: "Default: UTF-8" |
|
1036 | 1037 | text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo) |
|
1037 | 1038 | text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo) |
|
1038 | 1039 | text_scm_command: Command |
|
1039 | 1040 | text_scm_command_version: Version |
|
1040 | 1041 | text_scm_config: You can configure your SCM commands in config/configuration.yml. Please restart the application after editing it. |
|
1041 | 1042 | text_scm_command_not_available: SCM command is not available. Please check settings on the administration panel. |
|
1042 | 1043 | text_issue_conflict_resolution_overwrite: "Apply my changes anyway (previous notes will be kept but some changes may be overwritten)" |
|
1043 | 1044 | text_issue_conflict_resolution_add_notes: "Add my notes and discard my other changes" |
|
1044 | 1045 | text_issue_conflict_resolution_cancel: "Discard all my changes and redisplay %{link}" |
|
1045 | 1046 | text_account_destroy_confirmation: "Are you sure you want to proceed?\nYour account will be permanently deleted, with no way to reactivate it." |
|
1046 | 1047 | text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours." |
|
1047 | 1048 | text_project_closed: This project is closed and read-only. |
|
1048 | 1049 | text_turning_multiple_off: "If you disable multiple values, multiple values will be removed in order to preserve only one value per item." |
|
1049 | 1050 | |
|
1050 | 1051 | default_role_manager: Manager |
|
1051 | 1052 | default_role_developer: Developer |
|
1052 | 1053 | default_role_reporter: Reporter |
|
1053 | 1054 | default_tracker_bug: Bug |
|
1054 | 1055 | default_tracker_feature: Feature |
|
1055 | 1056 | default_tracker_support: Support |
|
1056 | 1057 | default_issue_status_new: New |
|
1057 | 1058 | default_issue_status_in_progress: In Progress |
|
1058 | 1059 | default_issue_status_resolved: Resolved |
|
1059 | 1060 | default_issue_status_feedback: Feedback |
|
1060 | 1061 | default_issue_status_closed: Closed |
|
1061 | 1062 | default_issue_status_rejected: Rejected |
|
1062 | 1063 | default_doc_category_user: User documentation |
|
1063 | 1064 | default_doc_category_tech: Technical documentation |
|
1064 | 1065 | default_priority_low: Low |
|
1065 | 1066 | default_priority_normal: Normal |
|
1066 | 1067 | default_priority_high: High |
|
1067 | 1068 | default_priority_urgent: Urgent |
|
1068 | 1069 | default_priority_immediate: Immediate |
|
1069 | 1070 | default_activity_design: Design |
|
1070 | 1071 | default_activity_development: Development |
|
1071 | 1072 | |
|
1072 | 1073 | enumeration_issue_priorities: Issue priorities |
|
1073 | 1074 | enumeration_doc_categories: Document categories |
|
1074 | 1075 | enumeration_activities: Activities (time tracking) |
|
1075 | 1076 | enumeration_system_activity: System Activity |
|
1076 | 1077 | description_filter: Filter |
|
1077 | 1078 | description_search: Searchfield |
|
1078 | 1079 | description_choose_project: Projects |
|
1079 | 1080 | description_project_scope: Search scope |
|
1080 | 1081 | description_notes: Notes |
|
1081 | 1082 | description_message_content: Message content |
|
1082 | 1083 | description_query_sort_criteria_attribute: Sort attribute |
|
1083 | 1084 | description_query_sort_criteria_direction: Sort direction |
|
1084 | 1085 | description_user_mail_notification: Mail notification settings |
|
1085 | 1086 | description_available_columns: Available Columns |
|
1086 | 1087 | description_selected_columns: Selected Columns |
|
1087 | 1088 | description_all_columns: All Columns |
|
1088 | 1089 | description_issue_category_reassign: Choose issue category |
|
1089 | 1090 | description_wiki_subpages_reassign: Choose new parent page |
|
1090 | 1091 | description_date_range_list: Choose range from list |
|
1091 | 1092 | description_date_range_interval: Choose range by selecting start and end date |
|
1092 | 1093 | description_date_from: Enter start date |
|
1093 | 1094 | description_date_to: Enter end date |
|
1094 | 1095 | 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,1112 +1,1113 | |||
|
1 | 1 | # French translations for Ruby on Rails |
|
2 | 2 | # by Christian Lescuyer (christian@flyingcoders.com) |
|
3 | 3 | # contributor: Sebastien Grosjean - ZenCocoon.com |
|
4 | 4 | # contributor: Thibaut Cuvelier - Developpez.com |
|
5 | 5 | |
|
6 | 6 | fr: |
|
7 | 7 | direction: ltr |
|
8 | 8 | date: |
|
9 | 9 | formats: |
|
10 | 10 | default: "%d/%m/%Y" |
|
11 | 11 | short: "%e %b" |
|
12 | 12 | long: "%e %B %Y" |
|
13 | 13 | long_ordinal: "%e %B %Y" |
|
14 | 14 | only_day: "%e" |
|
15 | 15 | |
|
16 | 16 | day_names: [dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi] |
|
17 | 17 | abbr_day_names: [dim, lun, mar, mer, jeu, ven, sam] |
|
18 | 18 | month_names: [~, janvier, fΓ©vrier, mars, avril, mai, juin, juillet, aoΓ»t, septembre, octobre, novembre, dΓ©cembre] |
|
19 | 19 | abbr_month_names: [~, jan., fΓ©v., mar., avr., mai, juin, juil., aoΓ»t, sept., oct., nov., dΓ©c.] |
|
20 | 20 | order: |
|
21 | 21 | - :day |
|
22 | 22 | - :month |
|
23 | 23 | - :year |
|
24 | 24 | |
|
25 | 25 | time: |
|
26 | 26 | formats: |
|
27 | 27 | default: "%d/%m/%Y %H:%M" |
|
28 | 28 | time: "%H:%M" |
|
29 | 29 | short: "%d %b %H:%M" |
|
30 | 30 | long: "%A %d %B %Y %H:%M:%S %Z" |
|
31 | 31 | long_ordinal: "%A %d %B %Y %H:%M:%S %Z" |
|
32 | 32 | only_second: "%S" |
|
33 | 33 | am: 'am' |
|
34 | 34 | pm: 'pm' |
|
35 | 35 | |
|
36 | 36 | datetime: |
|
37 | 37 | distance_in_words: |
|
38 | 38 | half_a_minute: "30 secondes" |
|
39 | 39 | less_than_x_seconds: |
|
40 | 40 | zero: "moins d'une seconde" |
|
41 | 41 | one: "moins d'uneΒ seconde" |
|
42 | 42 | other: "moins de %{count}Β secondes" |
|
43 | 43 | x_seconds: |
|
44 | 44 | one: "1Β seconde" |
|
45 | 45 | other: "%{count}Β secondes" |
|
46 | 46 | less_than_x_minutes: |
|
47 | 47 | zero: "moins d'une minute" |
|
48 | 48 | one: "moins d'uneΒ minute" |
|
49 | 49 | other: "moins de %{count}Β minutes" |
|
50 | 50 | x_minutes: |
|
51 | 51 | one: "1Β minute" |
|
52 | 52 | other: "%{count}Β minutes" |
|
53 | 53 | about_x_hours: |
|
54 | 54 | one: "environ une heure" |
|
55 | 55 | other: "environ %{count}Β heures" |
|
56 | 56 | x_hours: |
|
57 | 57 | one: "une heure" |
|
58 | 58 | other: "%{count}Β heures" |
|
59 | 59 | x_days: |
|
60 | 60 | one: "unΒ jour" |
|
61 | 61 | other: "%{count}Β jours" |
|
62 | 62 | about_x_months: |
|
63 | 63 | one: "environ un mois" |
|
64 | 64 | other: "environ %{count}Β mois" |
|
65 | 65 | x_months: |
|
66 | 66 | one: "unΒ mois" |
|
67 | 67 | other: "%{count}Β mois" |
|
68 | 68 | about_x_years: |
|
69 | 69 | one: "environ un an" |
|
70 | 70 | other: "environ %{count}Β ans" |
|
71 | 71 | over_x_years: |
|
72 | 72 | one: "plus d'un an" |
|
73 | 73 | other: "plus de %{count}Β ans" |
|
74 | 74 | almost_x_years: |
|
75 | 75 | one: "presqu'un an" |
|
76 | 76 | other: "presque %{count} ans" |
|
77 | 77 | prompts: |
|
78 | 78 | year: "AnnΓ©e" |
|
79 | 79 | month: "Mois" |
|
80 | 80 | day: "Jour" |
|
81 | 81 | hour: "Heure" |
|
82 | 82 | minute: "Minute" |
|
83 | 83 | second: "Seconde" |
|
84 | 84 | |
|
85 | 85 | number: |
|
86 | 86 | format: |
|
87 | 87 | precision: 3 |
|
88 | 88 | separator: ',' |
|
89 | 89 | delimiter: 'Β ' |
|
90 | 90 | currency: |
|
91 | 91 | format: |
|
92 | 92 | unit: 'β¬' |
|
93 | 93 | precision: 2 |
|
94 | 94 | format: '%nΒ %u' |
|
95 | 95 | human: |
|
96 | 96 | format: |
|
97 | 97 | precision: 3 |
|
98 | 98 | storage_units: |
|
99 | 99 | format: "%n %u" |
|
100 | 100 | units: |
|
101 | 101 | byte: |
|
102 | 102 | one: "octet" |
|
103 | 103 | other: "octet" |
|
104 | 104 | kb: "ko" |
|
105 | 105 | mb: "Mo" |
|
106 | 106 | gb: "Go" |
|
107 | 107 | tb: "To" |
|
108 | 108 | |
|
109 | 109 | support: |
|
110 | 110 | array: |
|
111 | 111 | sentence_connector: 'et' |
|
112 | 112 | skip_last_comma: true |
|
113 | 113 | word_connector: ", " |
|
114 | 114 | two_words_connector: " et " |
|
115 | 115 | last_word_connector: " et " |
|
116 | 116 | |
|
117 | 117 | activerecord: |
|
118 | 118 | errors: |
|
119 | 119 | template: |
|
120 | 120 | header: |
|
121 | 121 | one: "Impossible d'enregistrer %{model} : une erreur" |
|
122 | 122 | other: "Impossible d'enregistrer %{model} : %{count} erreurs." |
|
123 | 123 | body: "Veuillez vΓ©rifier les champs suivantsΒ :" |
|
124 | 124 | messages: |
|
125 | 125 | inclusion: "n'est pas inclus(e) dans la liste" |
|
126 | 126 | exclusion: "n'est pas disponible" |
|
127 | 127 | invalid: "n'est pas valide" |
|
128 | 128 | confirmation: "ne concorde pas avec la confirmation" |
|
129 | 129 | accepted: "doit Γͺtre acceptΓ©(e)" |
|
130 | 130 | empty: "doit Γͺtre renseignΓ©(e)" |
|
131 | 131 | blank: "doit Γͺtre renseignΓ©(e)" |
|
132 | 132 | too_long: "est trop long (pas plus de %{count} caractères)" |
|
133 | 133 | too_short: "est trop court (au moins %{count} caractères)" |
|
134 | 134 | wrong_length: "ne fait pas la bonne longueur (doit comporter %{count} caractères)" |
|
135 | 135 | taken: "est dΓ©jΓ utilisΓ©" |
|
136 | 136 | not_a_number: "n'est pas un nombre" |
|
137 | 137 | not_a_date: "n'est pas une date valide" |
|
138 | 138 | greater_than: "doit Γͺtre supΓ©rieur Γ %{count}" |
|
139 | 139 | greater_than_or_equal_to: "doit Γͺtre supΓ©rieur ou Γ©gal Γ %{count}" |
|
140 | 140 | equal_to: "doit Γͺtre Γ©gal Γ %{count}" |
|
141 | 141 | less_than: "doit Γͺtre infΓ©rieur Γ %{count}" |
|
142 | 142 | less_than_or_equal_to: "doit Γͺtre infΓ©rieur ou Γ©gal Γ %{count}" |
|
143 | 143 | odd: "doit Γͺtre impair" |
|
144 | 144 | even: "doit Γͺtre pair" |
|
145 | 145 | greater_than_start_date: "doit Γͺtre postΓ©rieure Γ la date de dΓ©but" |
|
146 | 146 | not_same_project: "n'appartient pas au mΓͺme projet" |
|
147 | 147 | circular_dependency: "Cette relation crΓ©erait une dΓ©pendance circulaire" |
|
148 | 148 | cant_link_an_issue_with_a_descendant: "Une demande ne peut pas Γͺtre liΓ©e Γ l'une de ses sous-tΓ’ches" |
|
149 | 149 | earlier_than_minimum_start_date: "ne peut pas Γͺtre antΓ©rieure au %{date} Γ cause des demandes qui prΓ©cΓ©dent" |
|
150 | 150 | |
|
151 | 151 | actionview_instancetag_blank_option: Choisir |
|
152 | 152 | |
|
153 | 153 | general_text_No: 'Non' |
|
154 | 154 | general_text_Yes: 'Oui' |
|
155 | 155 | general_text_no: 'non' |
|
156 | 156 | general_text_yes: 'oui' |
|
157 | 157 | general_lang_name: 'FranΓ§ais' |
|
158 | 158 | general_csv_separator: ';' |
|
159 | 159 | general_csv_decimal_separator: ',' |
|
160 | 160 | general_csv_encoding: ISO-8859-1 |
|
161 | 161 | general_pdf_encoding: UTF-8 |
|
162 | 162 | general_first_day_of_week: '1' |
|
163 | 163 | |
|
164 | 164 | notice_account_updated: Le compte a été mis à jour avec succès. |
|
165 | 165 | notice_account_invalid_creditentials: Identifiant ou mot de passe invalide. |
|
166 | 166 | notice_account_password_updated: Mot de passe mis à jour avec succès. |
|
167 | 167 | notice_account_wrong_password: Mot de passe incorrect |
|
168 | 168 | notice_account_register_done: Un message contenant les instructions pour activer votre compte vous a Γ©tΓ© envoyΓ© Γ l'adresse %{email}. |
|
169 | 169 | notice_account_unknown_email: Aucun compte ne correspond Γ cette adresse. |
|
170 | 170 | 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>. |
|
171 | 171 | notice_account_locked: Votre compte est verrouillΓ©. |
|
172 | 172 | notice_can_t_change_password: Ce compte utilise une authentification externe. Impossible de changer le mot de passe. |
|
173 | 173 | notice_account_lost_email_sent: Un message contenant les instructions pour choisir un nouveau mot de passe vous a Γ©tΓ© envoyΓ©. |
|
174 | 174 | notice_account_activated: Votre compte a Γ©tΓ© activΓ©. Vous pouvez Γ prΓ©sent vous connecter. |
|
175 | 175 | notice_successful_create: Création effectuée avec succès. |
|
176 | 176 | notice_successful_update: Mise à jour effectuée avec succès. |
|
177 | 177 | notice_successful_delete: Suppression effectuée avec succès. |
|
178 | 178 | notice_successful_connection: Connexion rΓ©ussie. |
|
179 | 179 | notice_file_not_found: "La page Γ laquelle vous souhaitez accΓ©der n'existe pas ou a Γ©tΓ© supprimΓ©e." |
|
180 | 180 | notice_locking_conflict: Les donnΓ©es ont Γ©tΓ© mises Γ jour par un autre utilisateur. Mise Γ jour impossible. |
|
181 | 181 | notice_not_authorized: "Vous n'Γͺtes pas autorisΓ© Γ accΓ©der Γ cette page." |
|
182 | 182 | notice_not_authorized_archived_project: Le projet auquel vous tentez d'accΓ©der a Γ©tΓ© archivΓ©. |
|
183 | 183 | notice_email_sent: "Un email a Γ©tΓ© envoyΓ© Γ %{value}" |
|
184 | 184 | notice_email_error: "Erreur lors de l'envoi de l'email (%{value})" |
|
185 | 185 | notice_feeds_access_key_reseted: "Votre clé d'accès aux flux Atom a été réinitialisée." |
|
186 | 186 | notice_failed_to_save_issues: "%{count} demande(s) sur les %{total} sΓ©lectionnΓ©es n'ont pas pu Γͺtre mise(s) Γ jour : %{ids}." |
|
187 | 187 | notice_failed_to_save_time_entries: "%{count} temps passΓ©(s) sur les %{total} sΓ©lectionnΓ©s n'ont pas pu Γͺtre mis Γ jour: %{ids}." |
|
188 | 188 | notice_no_issue_selected: "Aucune demande sΓ©lectionnΓ©e ! Cochez les demandes que vous voulez mettre Γ jour." |
|
189 | 189 | notice_account_pending: "Votre compte a été créé et attend l'approbation de l'administrateur." |
|
190 | 190 | notice_default_data_loaded: Paramétrage par défaut chargé avec succès. |
|
191 | 191 | notice_unable_delete_version: Impossible de supprimer cette version. |
|
192 | 192 | notice_issue_done_ratios_updated: L'avancement des demandes a Γ©tΓ© mis Γ jour. |
|
193 | 193 | notice_api_access_key_reseted: Votre clé d'accès API a été réinitialisée. |
|
194 | 194 | notice_gantt_chart_truncated: "Le diagramme a Γ©tΓ© tronquΓ© car il excΓ¨de le nombre maximal d'Γ©lΓ©ments pouvant Γͺtre affichΓ©s (%{max})" |
|
195 | 195 | notice_issue_successful_create: "Demande %{id} créée." |
|
196 | 196 | notice_issue_update_conflict: "La demande a Γ©tΓ© mise Γ jour par un autre utilisateur pendant que vous la modifiez." |
|
197 | 197 | notice_account_deleted: "Votre compte a Γ©tΓ© dΓ©finitivement supprimΓ©." |
|
198 | 198 | notice_user_successful_create: "Utilisateur %{id} créé." |
|
199 | 199 | notice_new_password_must_be_different: Votre nouveau mot de passe doit Γͺtre diffΓ©rent de votre mot de passe actuel |
|
200 | 200 | |
|
201 | 201 | error_can_t_load_default_data: "Une erreur s'est produite lors du chargement du paramΓ©trage : %{value}" |
|
202 | 202 | error_scm_not_found: "L'entrΓ©e et/ou la rΓ©vision demandΓ©e n'existe pas dans le dΓ©pΓ΄t." |
|
203 | 203 | error_scm_command_failed: "Une erreur s'est produite lors de l'accès au dépôt : %{value}" |
|
204 | 204 | error_scm_annotate: "L'entrΓ©e n'existe pas ou ne peut pas Γͺtre annotΓ©e." |
|
205 | 205 | error_issue_not_found_in_project: "La demande n'existe pas ou n'appartient pas Γ ce projet" |
|
206 | 206 | error_can_not_reopen_issue_on_closed_version: 'Une demande assignΓ©e Γ une version fermΓ©e ne peut pas Γͺtre rΓ©ouverte' |
|
207 | 207 | error_can_not_archive_project: "Ce projet ne peut pas Γͺtre archivΓ©" |
|
208 | 208 | error_workflow_copy_source: 'Veuillez sΓ©lectionner un tracker et/ou un rΓ΄le source' |
|
209 | 209 | error_workflow_copy_target: 'Veuillez sΓ©lectionner les trackers et rΓ΄les cibles' |
|
210 | 210 | error_issue_done_ratios_not_updated: L'avancement des demandes n'a pas pu Γͺtre mis Γ jour. |
|
211 | 211 | error_attachment_too_big: Ce fichier ne peut pas Γͺtre attachΓ© car il excΓ¨de la taille maximale autorisΓ©e (%{max_size}) |
|
212 | 212 | error_session_expired: "Votre session a expirΓ©. Veuillez vous reconnecter." |
|
213 | 213 | |
|
214 | 214 | warning_attachments_not_saved: "%{count} fichier(s) n'ont pas pu Γͺtre sauvegardΓ©s." |
|
215 | 215 | |
|
216 | 216 | mail_subject_lost_password: "Votre mot de passe %{value}" |
|
217 | 217 | mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :' |
|
218 | 218 | mail_subject_register: "Activation de votre compte %{value}" |
|
219 | 219 | mail_body_register: 'Pour activer votre compte, cliquez sur le lien suivant :' |
|
220 | 220 | mail_body_account_information_external: "Vous pouvez utiliser votre compte %{value} pour vous connecter." |
|
221 | 221 | mail_body_account_information: Paramètres de connexion de votre compte |
|
222 | 222 | mail_subject_account_activation_request: "Demande d'activation d'un compte %{value}" |
|
223 | 223 | mail_body_account_activation_request: "Un nouvel utilisateur (%{value}) s'est inscrit. Son compte nΓ©cessite votre approbation :" |
|
224 | 224 | mail_subject_reminder: "%{count} demande(s) arrivent Γ Γ©chΓ©ance (%{days})" |
|
225 | 225 | mail_body_reminder: "%{count} demande(s) qui vous sont assignΓ©es arrivent Γ Γ©chΓ©ance dans les %{days} prochains jours :" |
|
226 | 226 | mail_subject_wiki_content_added: "Page wiki '%{id}' ajoutΓ©e" |
|
227 | 227 | mail_body_wiki_content_added: "La page wiki '%{id}' a Γ©tΓ© ajoutΓ©e par %{author}." |
|
228 | 228 | mail_subject_wiki_content_updated: "Page wiki '%{id}' mise Γ jour" |
|
229 | 229 | mail_body_wiki_content_updated: "La page wiki '%{id}' a Γ©tΓ© mise Γ jour par %{author}." |
|
230 | 230 | |
|
231 | 231 | |
|
232 | 232 | field_name: Nom |
|
233 | 233 | field_description: Description |
|
234 | 234 | field_summary: RΓ©sumΓ© |
|
235 | 235 | field_is_required: Obligatoire |
|
236 | 236 | field_firstname: PrΓ©nom |
|
237 | 237 | field_lastname: Nom |
|
238 | 238 | field_mail: "Email " |
|
239 | 239 | field_filename: Fichier |
|
240 | 240 | field_filesize: Taille |
|
241 | 241 | field_downloads: TΓ©lΓ©chargements |
|
242 | 242 | field_author: Auteur |
|
243 | 243 | field_created_on: "Créé " |
|
244 | 244 | field_updated_on: "Mis-Γ -jour " |
|
245 | 245 | field_closed_on: FermΓ© |
|
246 | 246 | field_field_format: Format |
|
247 | 247 | field_is_for_all: Pour tous les projets |
|
248 | 248 | field_possible_values: Valeurs possibles |
|
249 | 249 | field_regexp: Expression régulière |
|
250 | 250 | field_min_length: Longueur minimum |
|
251 | 251 | field_max_length: Longueur maximum |
|
252 | 252 | field_value: Valeur |
|
253 | 253 | field_category: CatΓ©gorie |
|
254 | 254 | field_title: Titre |
|
255 | 255 | field_project: Projet |
|
256 | 256 | field_issue: Demande |
|
257 | 257 | field_status: Statut |
|
258 | 258 | field_notes: Notes |
|
259 | 259 | field_is_closed: Demande fermΓ©e |
|
260 | 260 | field_is_default: Valeur par dΓ©faut |
|
261 | 261 | field_tracker: Tracker |
|
262 | 262 | field_subject: Sujet |
|
263 | 263 | field_due_date: EchΓ©ance |
|
264 | 264 | field_assigned_to: AssignΓ© Γ |
|
265 | 265 | field_priority: PrioritΓ© |
|
266 | 266 | field_fixed_version: Version cible |
|
267 | 267 | field_user: Utilisateur |
|
268 | 268 | field_role: RΓ΄le |
|
269 | 269 | field_homepage: "Site web " |
|
270 | 270 | field_is_public: Public |
|
271 | 271 | field_parent: Sous-projet de |
|
272 | 272 | field_is_in_roadmap: Demandes affichΓ©es dans la roadmap |
|
273 | 273 | field_login: "Identifiant " |
|
274 | 274 | field_mail_notification: Notifications par mail |
|
275 | 275 | field_admin: Administrateur |
|
276 | 276 | field_last_login_on: "Dernière connexion " |
|
277 | 277 | field_language: Langue |
|
278 | 278 | field_effective_date: Date |
|
279 | 279 | field_password: Mot de passe |
|
280 | 280 | field_new_password: Nouveau mot de passe |
|
281 | 281 | field_password_confirmation: Confirmation |
|
282 | 282 | field_version: Version |
|
283 | 283 | field_type: Type |
|
284 | 284 | field_host: HΓ΄te |
|
285 | 285 | field_port: Port |
|
286 | 286 | field_account: Compte |
|
287 | 287 | field_base_dn: Base DN |
|
288 | 288 | field_attr_login: Attribut Identifiant |
|
289 | 289 | field_attr_firstname: Attribut PrΓ©nom |
|
290 | 290 | field_attr_lastname: Attribut Nom |
|
291 | 291 | field_attr_mail: Attribut Email |
|
292 | 292 | field_onthefly: CrΓ©ation des utilisateurs Γ la volΓ©e |
|
293 | 293 | field_start_date: DΓ©but |
|
294 | 294 | field_done_ratio: "% rΓ©alisΓ©" |
|
295 | 295 | field_auth_source: Mode d'authentification |
|
296 | 296 | field_hide_mail: Cacher mon adresse mail |
|
297 | 297 | field_comments: Commentaire |
|
298 | 298 | field_url: URL |
|
299 | 299 | field_start_page: Page de dΓ©marrage |
|
300 | 300 | field_subproject: Sous-projet |
|
301 | 301 | field_hours: Heures |
|
302 | 302 | field_activity: ActivitΓ© |
|
303 | 303 | field_spent_on: Date |
|
304 | 304 | field_identifier: Identifiant |
|
305 | 305 | field_is_filter: UtilisΓ© comme filtre |
|
306 | 306 | field_issue_to: Demande liΓ©e |
|
307 | 307 | field_delay: Retard |
|
308 | 308 | field_assignable: Demandes assignables Γ ce rΓ΄le |
|
309 | 309 | field_redirect_existing_links: Rediriger les liens existants |
|
310 | 310 | field_estimated_hours: Temps estimΓ© |
|
311 | 311 | field_column_names: Colonnes |
|
312 | 312 | field_time_zone: Fuseau horaire |
|
313 | 313 | field_searchable: UtilisΓ© pour les recherches |
|
314 | 314 | field_default_value: Valeur par dΓ©faut |
|
315 | 315 | field_comments_sorting: Afficher les commentaires |
|
316 | 316 | field_parent_title: Page parent |
|
317 | 317 | field_editable: Modifiable |
|
318 | 318 | field_watcher: Observateur |
|
319 | 319 | field_identity_url: URL OpenID |
|
320 | 320 | field_content: Contenu |
|
321 | 321 | field_group_by: Grouper par |
|
322 | 322 | field_sharing: Partage |
|
323 | 323 | field_active: Actif |
|
324 | 324 | field_parent_issue: TΓ’che parente |
|
325 | 325 | field_visible: Visible |
|
326 | 326 | field_warn_on_leaving_unsaved: "M'avertir lorsque je quitte une page contenant du texte non sauvegardΓ©" |
|
327 | 327 | field_issues_visibility: VisibilitΓ© des demandes |
|
328 | 328 | field_is_private: PrivΓ©e |
|
329 | 329 | field_commit_logs_encoding: Encodage des messages de commit |
|
330 | 330 | field_repository_is_default: DΓ©pΓ΄t principal |
|
331 | 331 | field_multiple: Valeurs multiples |
|
332 | 332 | field_auth_source_ldap_filter: Filtre LDAP |
|
333 | 333 | field_core_fields: Champs standards |
|
334 | 334 | field_timeout: "Timeout (en secondes)" |
|
335 | 335 | field_board_parent: Forum parent |
|
336 | 336 | field_private_notes: Notes privΓ©es |
|
337 | 337 | field_inherit_members: HΓ©riter les membres |
|
338 | 338 | field_generate_password: GΓ©nΓ©rer un mot de passe |
|
339 | 339 | field_must_change_passwd: Doit changer de mot de passe Γ la prochaine connexion |
|
340 | 340 | |
|
341 | 341 | setting_app_title: Titre de l'application |
|
342 | 342 | setting_app_subtitle: Sous-titre de l'application |
|
343 | 343 | setting_welcome_text: Texte d'accueil |
|
344 | 344 | setting_default_language: Langue par dΓ©faut |
|
345 | 345 | setting_login_required: Authentification obligatoire |
|
346 | 346 | setting_self_registration: Inscription des nouveaux utilisateurs |
|
347 | 347 | setting_attachment_max_size: Taille maximale des fichiers |
|
348 | 348 | setting_issues_export_limit: Limite d'exportation des demandes |
|
349 | 349 | setting_mail_from: Adresse d'Γ©mission |
|
350 | 350 | setting_bcc_recipients: Destinataires en copie cachΓ©e (cci) |
|
351 | 351 | setting_plain_text_mail: Mail en texte brut (non HTML) |
|
352 | 352 | setting_host_name: Nom d'hΓ΄te et chemin |
|
353 | 353 | setting_text_formatting: Formatage du texte |
|
354 | 354 | setting_wiki_compression: Compression de l'historique des pages wiki |
|
355 | 355 | setting_feeds_limit: Nombre maximal d'Γ©lΓ©ments dans les flux Atom |
|
356 | 356 | setting_default_projects_public: DΓ©finir les nouveaux projets comme publics par dΓ©faut |
|
357 | 357 | setting_autofetch_changesets: RΓ©cupΓ©ration automatique des commits |
|
358 | 358 | setting_sys_api_enabled: Activer les WS pour la gestion des dΓ©pΓ΄ts |
|
359 | 359 | setting_commit_ref_keywords: Mots-clΓ©s de rΓ©fΓ©rencement |
|
360 | 360 | setting_commit_fix_keywords: Mots-clΓ©s de rΓ©solution |
|
361 | 361 | setting_autologin: DurΓ©e maximale de connexion automatique |
|
362 | 362 | setting_date_format: Format de date |
|
363 | 363 | setting_time_format: Format d'heure |
|
364 | 364 | setting_cross_project_issue_relations: Autoriser les relations entre demandes de diffΓ©rents projets |
|
365 | 365 | setting_cross_project_subtasks: Autoriser les sous-tΓ’ches dans des projets diffΓ©rents |
|
366 | 366 | setting_issue_list_default_columns: Colonnes affichΓ©es par dΓ©faut sur la liste des demandes |
|
367 | 367 | setting_emails_footer: Pied-de-page des emails |
|
368 | 368 | setting_protocol: Protocole |
|
369 | 369 | setting_per_page_options: Options d'objets affichΓ©s par page |
|
370 | 370 | setting_user_format: Format d'affichage des utilisateurs |
|
371 | 371 | setting_activity_days_default: Nombre de jours affichΓ©s sur l'activitΓ© des projets |
|
372 | 372 | setting_display_subprojects_issues: Afficher par dΓ©faut les demandes des sous-projets sur les projets principaux |
|
373 | 373 | setting_enabled_scm: SCM activΓ©s |
|
374 | 374 | setting_mail_handler_body_delimiters: "Tronquer les emails après l'une de ces lignes" |
|
375 | 375 | setting_mail_handler_api_enabled: "Activer le WS pour la rΓ©ception d'emails" |
|
376 | 376 | setting_mail_handler_api_key: ClΓ© de protection de l'API |
|
377 | 377 | setting_sequential_project_identifiers: GΓ©nΓ©rer des identifiants de projet sΓ©quentiels |
|
378 | 378 | setting_gravatar_enabled: Afficher les Gravatar des utilisateurs |
|
379 | 379 | setting_diff_max_lines_displayed: Nombre maximum de lignes de diff affichΓ©es |
|
380 | 380 | setting_file_max_size_displayed: Taille maximum des fichiers texte affichΓ©s en ligne |
|
381 | 381 | setting_repository_log_display_limit: "Nombre maximum de rΓ©visions affichΓ©es sur l'historique d'un fichier" |
|
382 | 382 | setting_openid: "Autoriser l'authentification et l'enregistrement OpenID" |
|
383 | 383 | setting_password_min_length: Longueur minimum des mots de passe |
|
384 | 384 | setting_new_project_user_role_id: RΓ΄le donnΓ© Γ un utilisateur non-administrateur qui crΓ©e un projet |
|
385 | 385 | setting_default_projects_modules: Modules activΓ©s par dΓ©faut pour les nouveaux projets |
|
386 | 386 | setting_issue_done_ratio: Calcul de l'avancement des demandes |
|
387 | 387 | setting_issue_done_ratio_issue_status: Utiliser le statut |
|
388 | 388 | setting_issue_done_ratio_issue_field: 'Utiliser le champ % effectuΓ©' |
|
389 | 389 | setting_rest_api_enabled: Activer l'API REST |
|
390 | 390 | setting_gravatar_default: Image Gravatar par dΓ©faut |
|
391 | 391 | setting_start_of_week: Jour de dΓ©but des calendriers |
|
392 | 392 | setting_cache_formatted_text: Mettre en cache le texte formatΓ© |
|
393 | 393 | setting_commit_logtime_enabled: Permettre la saisie de temps |
|
394 | 394 | setting_commit_logtime_activity_id: ActivitΓ© pour le temps saisi |
|
395 | 395 | setting_gantt_items_limit: Nombre maximum d'Γ©lΓ©ments affichΓ©s sur le gantt |
|
396 | 396 | setting_issue_group_assignment: Permettre l'assignement des demandes aux groupes |
|
397 | 397 | 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 |
|
398 | 398 | setting_commit_cross_project_ref: Permettre le rΓ©fΓ©rencement et la rΓ©solution des demandes de tous les autres projets |
|
399 | 399 | setting_unsubscribe: Permettre aux utilisateurs de supprimer leur propre compte |
|
400 | 400 | setting_session_lifetime: DurΓ©e de vie maximale des sessions |
|
401 | 401 | setting_session_timeout: DurΓ©e maximale d'inactivitΓ© |
|
402 | 402 | setting_thumbnails_enabled: Afficher les vignettes des images |
|
403 | 403 | setting_thumbnails_size: Taille des vignettes (en pixels) |
|
404 | 404 | setting_non_working_week_days: Jours non travaillΓ©s |
|
405 | 405 | setting_jsonp_enabled: Activer le support JSONP |
|
406 | 406 | setting_default_projects_tracker_ids: Trackers par dΓ©faut pour les nouveaux projets |
|
407 | setting_mail_handler_excluded_filenames: Exclure les fichiers attachΓ©s par leur nom | |
|
407 | 408 | |
|
408 | 409 | permission_add_project: CrΓ©er un projet |
|
409 | 410 | permission_add_subprojects: CrΓ©er des sous-projets |
|
410 | 411 | permission_edit_project: Modifier le projet |
|
411 | 412 | permission_close_project: Fermer / rΓ©ouvrir le projet |
|
412 | 413 | permission_select_project_modules: Choisir les modules |
|
413 | 414 | permission_manage_members: GΓ©rer les membres |
|
414 | 415 | permission_manage_versions: GΓ©rer les versions |
|
415 | 416 | permission_manage_categories: GΓ©rer les catΓ©gories de demandes |
|
416 | 417 | permission_view_issues: Voir les demandes |
|
417 | 418 | permission_add_issues: CrΓ©er des demandes |
|
418 | 419 | permission_edit_issues: Modifier les demandes |
|
419 | 420 | permission_manage_issue_relations: GΓ©rer les relations |
|
420 | 421 | permission_set_issues_private: Rendre les demandes publiques ou privΓ©es |
|
421 | 422 | permission_set_own_issues_private: Rendre ses propres demandes publiques ou privΓ©es |
|
422 | 423 | permission_add_issue_notes: Ajouter des notes |
|
423 | 424 | permission_edit_issue_notes: Modifier les notes |
|
424 | 425 | permission_edit_own_issue_notes: Modifier ses propres notes |
|
425 | 426 | permission_view_private_notes: Voir les notes privΓ©es |
|
426 | 427 | permission_set_notes_private: Rendre les notes privΓ©es |
|
427 | 428 | permission_move_issues: DΓ©placer les demandes |
|
428 | 429 | permission_delete_issues: Supprimer les demandes |
|
429 | 430 | permission_manage_public_queries: GΓ©rer les requΓͺtes publiques |
|
430 | 431 | permission_save_queries: Sauvegarder les requΓͺtes |
|
431 | 432 | permission_view_gantt: Voir le gantt |
|
432 | 433 | permission_view_calendar: Voir le calendrier |
|
433 | 434 | permission_view_issue_watchers: Voir la liste des observateurs |
|
434 | 435 | permission_add_issue_watchers: Ajouter des observateurs |
|
435 | 436 | permission_delete_issue_watchers: Supprimer des observateurs |
|
436 | 437 | permission_log_time: Saisir le temps passΓ© |
|
437 | 438 | permission_view_time_entries: Voir le temps passΓ© |
|
438 | 439 | permission_edit_time_entries: Modifier les temps passΓ©s |
|
439 | 440 | permission_edit_own_time_entries: Modifier son propre temps passΓ© |
|
440 | 441 | permission_manage_news: GΓ©rer les annonces |
|
441 | 442 | permission_comment_news: Commenter les annonces |
|
442 | 443 | permission_view_documents: Voir les documents |
|
443 | 444 | permission_add_documents: Ajouter des documents |
|
444 | 445 | permission_edit_documents: Modifier les documents |
|
445 | 446 | permission_delete_documents: Supprimer les documents |
|
446 | 447 | permission_manage_files: GΓ©rer les fichiers |
|
447 | 448 | permission_view_files: Voir les fichiers |
|
448 | 449 | permission_manage_wiki: GΓ©rer le wiki |
|
449 | 450 | permission_rename_wiki_pages: Renommer les pages |
|
450 | 451 | permission_delete_wiki_pages: Supprimer les pages |
|
451 | 452 | permission_view_wiki_pages: Voir le wiki |
|
452 | 453 | permission_view_wiki_edits: "Voir l'historique des modifications" |
|
453 | 454 | permission_edit_wiki_pages: Modifier les pages |
|
454 | 455 | permission_delete_wiki_pages_attachments: Supprimer les fichiers joints |
|
455 | 456 | permission_protect_wiki_pages: ProtΓ©ger les pages |
|
456 | 457 | permission_manage_repository: GΓ©rer le dΓ©pΓ΄t de sources |
|
457 | 458 | permission_browse_repository: Parcourir les sources |
|
458 | 459 | permission_view_changesets: Voir les rΓ©visions |
|
459 | 460 | permission_commit_access: Droit de commit |
|
460 | 461 | permission_manage_boards: GΓ©rer les forums |
|
461 | 462 | permission_view_messages: Voir les messages |
|
462 | 463 | permission_add_messages: Poster un message |
|
463 | 464 | permission_edit_messages: Modifier les messages |
|
464 | 465 | permission_edit_own_messages: Modifier ses propres messages |
|
465 | 466 | permission_delete_messages: Supprimer les messages |
|
466 | 467 | permission_delete_own_messages: Supprimer ses propres messages |
|
467 | 468 | permission_export_wiki_pages: Exporter les pages |
|
468 | 469 | permission_manage_project_activities: GΓ©rer les activitΓ©s |
|
469 | 470 | permission_manage_subtasks: GΓ©rer les sous-tΓ’ches |
|
470 | 471 | permission_manage_related_issues: GΓ©rer les demandes associΓ©es |
|
471 | 472 | |
|
472 | 473 | project_module_issue_tracking: Suivi des demandes |
|
473 | 474 | project_module_time_tracking: Suivi du temps passΓ© |
|
474 | 475 | project_module_news: Publication d'annonces |
|
475 | 476 | project_module_documents: Publication de documents |
|
476 | 477 | project_module_files: Publication de fichiers |
|
477 | 478 | project_module_wiki: Wiki |
|
478 | 479 | project_module_repository: DΓ©pΓ΄t de sources |
|
479 | 480 | project_module_boards: Forums de discussion |
|
480 | 481 | |
|
481 | 482 | label_user: Utilisateur |
|
482 | 483 | label_user_plural: Utilisateurs |
|
483 | 484 | label_user_new: Nouvel utilisateur |
|
484 | 485 | label_user_anonymous: Anonyme |
|
485 | 486 | label_project: Projet |
|
486 | 487 | label_project_new: Nouveau projet |
|
487 | 488 | label_project_plural: Projets |
|
488 | 489 | label_x_projects: |
|
489 | 490 | zero: aucun projet |
|
490 | 491 | one: un projet |
|
491 | 492 | other: "%{count} projets" |
|
492 | 493 | label_project_all: Tous les projets |
|
493 | 494 | label_project_latest: Derniers projets |
|
494 | 495 | label_issue: Demande |
|
495 | 496 | label_issue_new: Nouvelle demande |
|
496 | 497 | label_issue_plural: Demandes |
|
497 | 498 | label_issue_view_all: Voir toutes les demandes |
|
498 | 499 | label_issue_added: Demande ajoutΓ©e |
|
499 | 500 | label_issue_updated: Demande mise Γ jour |
|
500 | 501 | label_issue_note_added: Note ajoutΓ©e |
|
501 | 502 | label_issue_status_updated: Statut changΓ© |
|
502 | 503 | label_issue_priority_updated: PrioritΓ© changΓ©e |
|
503 | 504 | label_issues_by: "Demandes par %{value}" |
|
504 | 505 | label_document: Document |
|
505 | 506 | label_document_new: Nouveau document |
|
506 | 507 | label_document_plural: Documents |
|
507 | 508 | label_document_added: Document ajoutΓ© |
|
508 | 509 | label_role: RΓ΄le |
|
509 | 510 | label_role_plural: RΓ΄les |
|
510 | 511 | label_role_new: Nouveau rΓ΄le |
|
511 | 512 | label_role_and_permissions: RΓ΄les et permissions |
|
512 | 513 | label_role_anonymous: Anonyme |
|
513 | 514 | label_role_non_member: Non membre |
|
514 | 515 | label_member: Membre |
|
515 | 516 | label_member_new: Nouveau membre |
|
516 | 517 | label_member_plural: Membres |
|
517 | 518 | label_tracker: Tracker |
|
518 | 519 | label_tracker_plural: Trackers |
|
519 | 520 | label_tracker_new: Nouveau tracker |
|
520 | 521 | label_workflow: Workflow |
|
521 | 522 | label_issue_status: Statut de demandes |
|
522 | 523 | label_issue_status_plural: Statuts de demandes |
|
523 | 524 | label_issue_status_new: Nouveau statut |
|
524 | 525 | label_issue_category: CatΓ©gorie de demandes |
|
525 | 526 | label_issue_category_plural: CatΓ©gories de demandes |
|
526 | 527 | label_issue_category_new: Nouvelle catΓ©gorie |
|
527 | 528 | label_custom_field: Champ personnalisΓ© |
|
528 | 529 | label_custom_field_plural: Champs personnalisΓ©s |
|
529 | 530 | label_custom_field_new: Nouveau champ personnalisΓ© |
|
530 | 531 | label_enumerations: Listes de valeurs |
|
531 | 532 | label_enumeration_new: Nouvelle valeur |
|
532 | 533 | label_information: Information |
|
533 | 534 | label_information_plural: Informations |
|
534 | 535 | label_please_login: Identification |
|
535 | 536 | label_register: S'enregistrer |
|
536 | 537 | label_login_with_open_id_option: S'authentifier avec OpenID |
|
537 | 538 | label_password_lost: Mot de passe perdu |
|
538 | 539 | label_home: Accueil |
|
539 | 540 | label_my_page: Ma page |
|
540 | 541 | label_my_account: Mon compte |
|
541 | 542 | label_my_projects: Mes projets |
|
542 | 543 | label_my_page_block: Blocs disponibles |
|
543 | 544 | label_administration: Administration |
|
544 | 545 | label_login: Connexion |
|
545 | 546 | label_logout: DΓ©connexion |
|
546 | 547 | label_help: Aide |
|
547 | 548 | label_reported_issues: "Demandes soumises " |
|
548 | 549 | label_assigned_to_me_issues: Demandes qui me sont assignΓ©es |
|
549 | 550 | label_last_login: "Dernière connexion " |
|
550 | 551 | label_registered_on: "Inscrit le " |
|
551 | 552 | label_activity: ActivitΓ© |
|
552 | 553 | label_overall_activity: ActivitΓ© globale |
|
553 | 554 | label_user_activity: "ActivitΓ© de %{value}" |
|
554 | 555 | label_new: Nouveau |
|
555 | 556 | label_logged_as: ConnectΓ© en tant que |
|
556 | 557 | label_environment: Environnement |
|
557 | 558 | label_authentication: Authentification |
|
558 | 559 | label_auth_source: Mode d'authentification |
|
559 | 560 | label_auth_source_new: Nouveau mode d'authentification |
|
560 | 561 | label_auth_source_plural: Modes d'authentification |
|
561 | 562 | label_subproject_plural: Sous-projets |
|
562 | 563 | label_subproject_new: Nouveau sous-projet |
|
563 | 564 | label_and_its_subprojects: "%{value} et ses sous-projets" |
|
564 | 565 | label_min_max_length: Longueurs mini - maxi |
|
565 | 566 | label_list: Liste |
|
566 | 567 | label_date: Date |
|
567 | 568 | label_integer: Entier |
|
568 | 569 | label_float: Nombre dΓ©cimal |
|
569 | 570 | label_boolean: BoolΓ©en |
|
570 | 571 | label_string: Texte |
|
571 | 572 | label_text: Texte long |
|
572 | 573 | label_attribute: Attribut |
|
573 | 574 | label_attribute_plural: Attributs |
|
574 | 575 | label_no_data: Aucune donnΓ©e Γ afficher |
|
575 | 576 | label_change_status: Changer le statut |
|
576 | 577 | label_history: Historique |
|
577 | 578 | label_attachment: Fichier |
|
578 | 579 | label_attachment_new: Nouveau fichier |
|
579 | 580 | label_attachment_delete: Supprimer le fichier |
|
580 | 581 | label_attachment_plural: Fichiers |
|
581 | 582 | label_file_added: Fichier ajoutΓ© |
|
582 | 583 | label_report: Rapport |
|
583 | 584 | label_report_plural: Rapports |
|
584 | 585 | label_news: Annonce |
|
585 | 586 | label_news_new: Nouvelle annonce |
|
586 | 587 | label_news_plural: Annonces |
|
587 | 588 | label_news_latest: Dernières annonces |
|
588 | 589 | label_news_view_all: Voir toutes les annonces |
|
589 | 590 | label_news_added: Annonce ajoutΓ©e |
|
590 | 591 | label_news_comment_added: Commentaire ajoutΓ© Γ une annonce |
|
591 | 592 | label_settings: Configuration |
|
592 | 593 | label_overview: AperΓ§u |
|
593 | 594 | label_version: Version |
|
594 | 595 | label_version_new: Nouvelle version |
|
595 | 596 | label_version_plural: Versions |
|
596 | 597 | label_confirmation: Confirmation |
|
597 | 598 | label_export_to: 'Formats disponibles :' |
|
598 | 599 | label_read: Lire... |
|
599 | 600 | label_public_projects: Projets publics |
|
600 | 601 | label_open_issues: ouvert |
|
601 | 602 | label_open_issues_plural: ouverts |
|
602 | 603 | label_closed_issues: fermΓ© |
|
603 | 604 | label_closed_issues_plural: fermΓ©s |
|
604 | 605 | label_x_open_issues_abbr_on_total: |
|
605 | 606 | zero: 0 ouverte sur %{total} |
|
606 | 607 | one: 1 ouverte sur %{total} |
|
607 | 608 | other: "%{count} ouvertes sur %{total}" |
|
608 | 609 | label_x_open_issues_abbr: |
|
609 | 610 | zero: 0 ouverte |
|
610 | 611 | one: 1 ouverte |
|
611 | 612 | other: "%{count} ouvertes" |
|
612 | 613 | label_x_closed_issues_abbr: |
|
613 | 614 | zero: 0 fermΓ©e |
|
614 | 615 | one: 1 fermΓ©e |
|
615 | 616 | other: "%{count} fermΓ©es" |
|
616 | 617 | label_x_issues: |
|
617 | 618 | zero: 0 demande |
|
618 | 619 | one: 1 demande |
|
619 | 620 | other: "%{count} demandes" |
|
620 | 621 | label_total: Total |
|
621 | 622 | label_total_time: Temps total |
|
622 | 623 | label_permissions: Permissions |
|
623 | 624 | label_current_status: Statut actuel |
|
624 | 625 | label_new_statuses_allowed: Nouveaux statuts autorisΓ©s |
|
625 | 626 | label_all: tous |
|
626 | 627 | label_any: tous |
|
627 | 628 | label_none: aucun |
|
628 | 629 | label_nobody: personne |
|
629 | 630 | label_next: Suivant |
|
630 | 631 | label_previous: PrΓ©cΓ©dent |
|
631 | 632 | label_used_by: UtilisΓ© par |
|
632 | 633 | label_details: DΓ©tails |
|
633 | 634 | label_add_note: Ajouter une note |
|
634 | 635 | label_per_page: Par page |
|
635 | 636 | label_calendar: Calendrier |
|
636 | 637 | label_months_from: mois depuis |
|
637 | 638 | label_gantt: Gantt |
|
638 | 639 | label_internal: Interne |
|
639 | 640 | label_last_changes: "%{count} derniers changements" |
|
640 | 641 | label_change_view_all: Voir tous les changements |
|
641 | 642 | label_personalize_page: Personnaliser cette page |
|
642 | 643 | label_comment: Commentaire |
|
643 | 644 | label_comment_plural: Commentaires |
|
644 | 645 | label_x_comments: |
|
645 | 646 | zero: aucun commentaire |
|
646 | 647 | one: un commentaire |
|
647 | 648 | other: "%{count} commentaires" |
|
648 | 649 | label_comment_add: Ajouter un commentaire |
|
649 | 650 | label_comment_added: Commentaire ajoutΓ© |
|
650 | 651 | label_comment_delete: Supprimer les commentaires |
|
651 | 652 | label_query: Rapport personnalisΓ© |
|
652 | 653 | label_query_plural: Rapports personnalisΓ©s |
|
653 | 654 | label_query_new: Nouveau rapport |
|
654 | 655 | label_my_queries: Mes rapports personnalisΓ©s |
|
655 | 656 | label_filter_add: "Ajouter le filtre " |
|
656 | 657 | label_filter_plural: Filtres |
|
657 | 658 | label_equals: Γ©gal |
|
658 | 659 | label_not_equals: diffΓ©rent |
|
659 | 660 | label_in_less_than: dans moins de |
|
660 | 661 | label_in_more_than: dans plus de |
|
661 | 662 | label_in_the_next_days: dans les prochains jours |
|
662 | 663 | label_in_the_past_days: dans les derniers jours |
|
663 | 664 | label_in: dans |
|
664 | 665 | label_today: aujourd'hui |
|
665 | 666 | label_all_time: toute la pΓ©riode |
|
666 | 667 | label_yesterday: hier |
|
667 | 668 | label_this_week: cette semaine |
|
668 | 669 | label_last_week: la semaine dernière |
|
669 | 670 | label_last_n_weeks: "les %{count} dernières semaines" |
|
670 | 671 | label_last_n_days: "les %{count} derniers jours" |
|
671 | 672 | label_this_month: ce mois-ci |
|
672 | 673 | label_last_month: le mois dernier |
|
673 | 674 | label_this_year: cette annΓ©e |
|
674 | 675 | label_date_range: PΓ©riode |
|
675 | 676 | label_less_than_ago: il y a moins de |
|
676 | 677 | label_more_than_ago: il y a plus de |
|
677 | 678 | label_ago: il y a |
|
678 | 679 | label_contains: contient |
|
679 | 680 | label_not_contains: ne contient pas |
|
680 | 681 | label_any_issues_in_project: une demande du projet |
|
681 | 682 | label_any_issues_not_in_project: une demande hors du projet |
|
682 | 683 | label_no_issues_in_project: aucune demande du projet |
|
683 | 684 | label_day_plural: jours |
|
684 | 685 | label_repository: DΓ©pΓ΄t |
|
685 | 686 | label_repository_new: Nouveau dΓ©pΓ΄t |
|
686 | 687 | label_repository_plural: DΓ©pΓ΄ts |
|
687 | 688 | label_browse: Parcourir |
|
688 | 689 | label_revision: "RΓ©vision " |
|
689 | 690 | label_revision_plural: RΓ©visions |
|
690 | 691 | label_associated_revisions: RΓ©visions associΓ©es |
|
691 | 692 | label_added: ajoutΓ© |
|
692 | 693 | label_modified: modifiΓ© |
|
693 | 694 | label_copied: copiΓ© |
|
694 | 695 | label_renamed: renommΓ© |
|
695 | 696 | label_deleted: supprimΓ© |
|
696 | 697 | label_latest_revision: Dernière révision |
|
697 | 698 | label_latest_revision_plural: Dernières révisions |
|
698 | 699 | label_view_revisions: Voir les rΓ©visions |
|
699 | 700 | label_max_size: Taille maximale |
|
700 | 701 | label_sort_highest: Remonter en premier |
|
701 | 702 | label_sort_higher: Remonter |
|
702 | 703 | label_sort_lower: Descendre |
|
703 | 704 | label_sort_lowest: Descendre en dernier |
|
704 | 705 | label_roadmap: Roadmap |
|
705 | 706 | label_roadmap_due_in: "ΓchΓ©ance dans %{value}" |
|
706 | 707 | label_roadmap_overdue: "En retard de %{value}" |
|
707 | 708 | label_roadmap_no_issues: Aucune demande pour cette version |
|
708 | 709 | label_search: "Recherche " |
|
709 | 710 | label_result_plural: RΓ©sultats |
|
710 | 711 | label_all_words: Tous les mots |
|
711 | 712 | label_wiki: Wiki |
|
712 | 713 | label_wiki_edit: RΓ©vision wiki |
|
713 | 714 | label_wiki_edit_plural: RΓ©visions wiki |
|
714 | 715 | label_wiki_page: Page wiki |
|
715 | 716 | label_wiki_page_plural: Pages wiki |
|
716 | 717 | label_index_by_title: Index par titre |
|
717 | 718 | label_index_by_date: Index par date |
|
718 | 719 | label_current_version: Version actuelle |
|
719 | 720 | label_preview: PrΓ©visualisation |
|
720 | 721 | label_feed_plural: Flux Atom |
|
721 | 722 | label_changes_details: DΓ©tails de tous les changements |
|
722 | 723 | label_issue_tracking: Suivi des demandes |
|
723 | 724 | label_spent_time: Temps passΓ© |
|
724 | 725 | label_f_hour: "%{value} heure" |
|
725 | 726 | label_f_hour_plural: "%{value} heures" |
|
726 | 727 | label_time_tracking: Suivi du temps |
|
727 | 728 | label_change_plural: Changements |
|
728 | 729 | label_statistics: Statistiques |
|
729 | 730 | label_commits_per_month: Commits par mois |
|
730 | 731 | label_commits_per_author: Commits par auteur |
|
731 | 732 | label_view_diff: Voir les diffΓ©rences |
|
732 | 733 | label_diff_inline: en ligne |
|
733 | 734 | label_diff_side_by_side: cΓ΄te Γ cΓ΄te |
|
734 | 735 | label_options: Options |
|
735 | 736 | label_copy_workflow_from: Copier le workflow de |
|
736 | 737 | label_permissions_report: Synthèse des permissions |
|
737 | 738 | label_watched_issues: Demandes surveillΓ©es |
|
738 | 739 | label_related_issues: Demandes liΓ©es |
|
739 | 740 | label_applied_status: Statut appliquΓ© |
|
740 | 741 | label_loading: Chargement... |
|
741 | 742 | label_relation_new: Nouvelle relation |
|
742 | 743 | label_relation_delete: Supprimer la relation |
|
743 | 744 | label_relates_to: LiΓ© Γ |
|
744 | 745 | label_duplicates: Duplique |
|
745 | 746 | label_duplicated_by: DupliquΓ© par |
|
746 | 747 | label_blocks: Bloque |
|
747 | 748 | label_blocked_by: BloquΓ© par |
|
748 | 749 | label_precedes: Précède |
|
749 | 750 | label_follows: Suit |
|
750 | 751 | label_copied_to: CopiΓ© vers |
|
751 | 752 | label_copied_from: CopiΓ© depuis |
|
752 | 753 | label_end_to_start: fin Γ dΓ©but |
|
753 | 754 | label_end_to_end: fin Γ fin |
|
754 | 755 | label_start_to_start: dΓ©but Γ dΓ©but |
|
755 | 756 | label_start_to_end: dΓ©but Γ fin |
|
756 | 757 | label_stay_logged_in: Rester connectΓ© |
|
757 | 758 | label_disabled: dΓ©sactivΓ© |
|
758 | 759 | label_show_completed_versions: Voir les versions passΓ©es |
|
759 | 760 | label_me: moi |
|
760 | 761 | label_board: Forum |
|
761 | 762 | label_board_new: Nouveau forum |
|
762 | 763 | label_board_plural: Forums |
|
763 | 764 | label_topic_plural: Discussions |
|
764 | 765 | label_message_plural: Messages |
|
765 | 766 | label_message_last: Dernier message |
|
766 | 767 | label_message_new: Nouveau message |
|
767 | 768 | label_message_posted: Message ajoutΓ© |
|
768 | 769 | label_reply_plural: RΓ©ponses |
|
769 | 770 | label_send_information: Envoyer les informations Γ l'utilisateur |
|
770 | 771 | label_year: AnnΓ©e |
|
771 | 772 | label_month: Mois |
|
772 | 773 | label_week: Semaine |
|
773 | 774 | label_date_from: Du |
|
774 | 775 | label_date_to: Au |
|
775 | 776 | label_language_based: BasΓ© sur la langue de l'utilisateur |
|
776 | 777 | label_sort_by: "Trier par %{value}" |
|
777 | 778 | label_send_test_email: Envoyer un email de test |
|
778 | 779 | label_feeds_access_key_created_on: "Clé d'accès Atom créée il y a %{value}" |
|
779 | 780 | label_module_plural: Modules |
|
780 | 781 | label_added_time_by: "AjoutΓ© par %{author} il y a %{age}" |
|
781 | 782 | label_updated_time_by: "Mis Γ jour par %{author} il y a %{age}" |
|
782 | 783 | label_updated_time: "Mis Γ jour il y a %{value}" |
|
783 | 784 | label_jump_to_a_project: Aller Γ un projet... |
|
784 | 785 | label_file_plural: Fichiers |
|
785 | 786 | label_changeset_plural: RΓ©visions |
|
786 | 787 | label_default_columns: Colonnes par dΓ©faut |
|
787 | 788 | label_no_change_option: (Pas de changement) |
|
788 | 789 | label_bulk_edit_selected_issues: Modifier les demandes sΓ©lectionnΓ©es |
|
789 | 790 | label_theme: Thème |
|
790 | 791 | label_default: DΓ©faut |
|
791 | 792 | label_search_titles_only: Uniquement dans les titres |
|
792 | 793 | label_user_mail_option_all: "Pour tous les Γ©vΓ©nements de tous mes projets" |
|
793 | 794 | label_user_mail_option_selected: "Pour tous les Γ©vΓ©nements des projets sΓ©lectionnΓ©s..." |
|
794 | 795 | label_user_mail_no_self_notified: "Je ne veux pas Γͺtre notifiΓ© des changements que j'effectue" |
|
795 | 796 | label_registration_activation_by_email: activation du compte par email |
|
796 | 797 | label_registration_manual_activation: activation manuelle du compte |
|
797 | 798 | label_registration_automatic_activation: activation automatique du compte |
|
798 | 799 | label_display_per_page: "Par page : %{value}" |
|
799 | 800 | label_age: Γge |
|
800 | 801 | label_change_properties: Changer les propriΓ©tΓ©s |
|
801 | 802 | label_general: GΓ©nΓ©ral |
|
802 | 803 | label_more: Plus |
|
803 | 804 | label_scm: SCM |
|
804 | 805 | label_plugins: Plugins |
|
805 | 806 | label_ldap_authentication: Authentification LDAP |
|
806 | 807 | label_downloads_abbr: D/L |
|
807 | 808 | label_optional_description: Description facultative |
|
808 | 809 | label_add_another_file: Ajouter un autre fichier |
|
809 | 810 | label_preferences: PrΓ©fΓ©rences |
|
810 | 811 | label_chronological_order: Dans l'ordre chronologique |
|
811 | 812 | label_reverse_chronological_order: Dans l'ordre chronologique inverse |
|
812 | 813 | label_planning: Planning |
|
813 | 814 | label_incoming_emails: Emails entrants |
|
814 | 815 | label_generate_key: GΓ©nΓ©rer une clΓ© |
|
815 | 816 | label_issue_watchers: Observateurs |
|
816 | 817 | label_example: Exemple |
|
817 | 818 | label_display: Affichage |
|
818 | 819 | label_sort: Tri |
|
819 | 820 | label_ascending: Croissant |
|
820 | 821 | label_descending: DΓ©croissant |
|
821 | 822 | label_date_from_to: Du %{start} au %{end} |
|
822 | 823 | label_wiki_content_added: Page wiki ajoutΓ©e |
|
823 | 824 | label_wiki_content_updated: Page wiki mise Γ jour |
|
824 | 825 | label_group_plural: Groupes |
|
825 | 826 | label_group: Groupe |
|
826 | 827 | label_group_new: Nouveau groupe |
|
827 | 828 | label_time_entry_plural: Temps passΓ© |
|
828 | 829 | label_version_sharing_none: Non partagΓ© |
|
829 | 830 | label_version_sharing_descendants: Avec les sous-projets |
|
830 | 831 | label_version_sharing_hierarchy: Avec toute la hiΓ©rarchie |
|
831 | 832 | label_version_sharing_tree: Avec tout l'arbre |
|
832 | 833 | label_version_sharing_system: Avec tous les projets |
|
833 | 834 | label_copy_source: Source |
|
834 | 835 | label_copy_target: Cible |
|
835 | 836 | label_copy_same_as_target: Comme la cible |
|
836 | 837 | label_update_issue_done_ratios: Mettre Γ jour l'avancement des demandes |
|
837 | 838 | label_display_used_statuses_only: N'afficher que les statuts utilisΓ©s dans ce tracker |
|
838 | 839 | label_api_access_key: Clé d'accès API |
|
839 | 840 | label_api_access_key_created_on: Clé d'accès API créée il y a %{value} |
|
840 | 841 | label_feeds_access_key: Clé d'accès Atom |
|
841 | 842 | label_missing_api_access_key: Clé d'accès API manquante |
|
842 | 843 | label_missing_feeds_access_key: Clé d'accès Atom manquante |
|
843 | 844 | label_close_versions: Fermer les versions terminΓ©es |
|
844 | 845 | label_revision_id: RΓ©vision %{value} |
|
845 | 846 | label_profile: Profil |
|
846 | 847 | label_subtask_plural: Sous-tΓ’ches |
|
847 | 848 | label_project_copy_notifications: Envoyer les notifications durant la copie du projet |
|
848 | 849 | label_principal_search: "Rechercher un utilisateur ou un groupe :" |
|
849 | 850 | label_user_search: "Rechercher un utilisateur :" |
|
850 | 851 | label_additional_workflow_transitions_for_author: Autorisations supplémentaires lorsque l'utilisateur a créé la demande |
|
851 | 852 | label_additional_workflow_transitions_for_assignee: Autorisations supplΓ©mentaires lorsque la demande est assignΓ©e Γ l'utilisateur |
|
852 | 853 | label_issues_visibility_all: Toutes les demandes |
|
853 | 854 | label_issues_visibility_public: Toutes les demandes non privΓ©es |
|
854 | 855 | label_issues_visibility_own: Demandes créées par ou assignées à l'utilisateur |
|
855 | 856 | label_export_options: Options d'exportation %{export_format} |
|
856 | 857 | label_copy_attachments: Copier les fichiers |
|
857 | 858 | label_copy_subtasks: Copier les sous-tΓ’ches |
|
858 | 859 | label_item_position: "%{position} sur %{count}" |
|
859 | 860 | label_completed_versions: Versions passΓ©es |
|
860 | 861 | label_session_expiration: Expiration des sessions |
|
861 | 862 | label_show_closed_projects: Voir les projets fermΓ©s |
|
862 | 863 | label_status_transitions: Changements de statut |
|
863 | 864 | label_fields_permissions: Permissions sur les champs |
|
864 | 865 | label_readonly: Lecture |
|
865 | 866 | label_required: Obligatoire |
|
866 | 867 | label_hidden: CachΓ© |
|
867 | 868 | label_attribute_of_project: "%{name} du projet" |
|
868 | 869 | label_attribute_of_issue: "%{name} de la demande" |
|
869 | 870 | label_attribute_of_author: "%{name} de l'auteur" |
|
870 | 871 | label_attribute_of_assigned_to: "%{name} de l'assignΓ©" |
|
871 | 872 | label_attribute_of_user: "%{name} de l'utilisateur" |
|
872 | 873 | label_attribute_of_fixed_version: "%{name} de la version cible" |
|
873 | 874 | label_cross_project_descendants: Avec les sous-projets |
|
874 | 875 | label_cross_project_tree: Avec tout l'arbre |
|
875 | 876 | label_cross_project_hierarchy: Avec toute la hiΓ©rarchie |
|
876 | 877 | label_cross_project_system: Avec tous les projets |
|
877 | 878 | label_gantt_progress_line: Ligne de progression |
|
878 | 879 | label_visibility_private: par moi uniquement |
|
879 | 880 | label_visibility_roles: par ces roles uniquement |
|
880 | 881 | label_visibility_public: par tout le monde |
|
881 | 882 | |
|
882 | 883 | button_login: Connexion |
|
883 | 884 | button_submit: Soumettre |
|
884 | 885 | button_save: Sauvegarder |
|
885 | 886 | button_check_all: Tout cocher |
|
886 | 887 | button_uncheck_all: Tout dΓ©cocher |
|
887 | 888 | button_collapse_all: Plier tout |
|
888 | 889 | button_expand_all: DΓ©plier tout |
|
889 | 890 | button_delete: Supprimer |
|
890 | 891 | button_create: CrΓ©er |
|
891 | 892 | button_create_and_continue: CrΓ©er et continuer |
|
892 | 893 | button_test: Tester |
|
893 | 894 | button_edit: Modifier |
|
894 | 895 | button_add: Ajouter |
|
895 | 896 | button_change: Changer |
|
896 | 897 | button_apply: Appliquer |
|
897 | 898 | button_clear: Effacer |
|
898 | 899 | button_lock: Verrouiller |
|
899 | 900 | button_unlock: DΓ©verrouiller |
|
900 | 901 | button_download: TΓ©lΓ©charger |
|
901 | 902 | button_list: Lister |
|
902 | 903 | button_view: Voir |
|
903 | 904 | button_move: DΓ©placer |
|
904 | 905 | button_move_and_follow: DΓ©placer et suivre |
|
905 | 906 | button_back: Retour |
|
906 | 907 | button_cancel: Annuler |
|
907 | 908 | button_activate: Activer |
|
908 | 909 | button_sort: Trier |
|
909 | 910 | button_log_time: Saisir temps |
|
910 | 911 | button_rollback: Revenir Γ cette version |
|
911 | 912 | button_watch: Surveiller |
|
912 | 913 | button_unwatch: Ne plus surveiller |
|
913 | 914 | button_reply: RΓ©pondre |
|
914 | 915 | button_archive: Archiver |
|
915 | 916 | button_unarchive: DΓ©sarchiver |
|
916 | 917 | button_reset: RΓ©initialiser |
|
917 | 918 | button_rename: Renommer |
|
918 | 919 | button_change_password: Changer de mot de passe |
|
919 | 920 | button_copy: Copier |
|
920 | 921 | button_copy_and_follow: Copier et suivre |
|
921 | 922 | button_annotate: Annoter |
|
922 | 923 | button_update: Mettre Γ jour |
|
923 | 924 | button_configure: Configurer |
|
924 | 925 | button_quote: Citer |
|
925 | 926 | button_duplicate: Dupliquer |
|
926 | 927 | button_show: Afficher |
|
927 | 928 | button_hide: Cacher |
|
928 | 929 | button_edit_section: Modifier cette section |
|
929 | 930 | button_export: Exporter |
|
930 | 931 | button_delete_my_account: Supprimer mon compte |
|
931 | 932 | button_close: Fermer |
|
932 | 933 | button_reopen: RΓ©ouvrir |
|
933 | 934 | |
|
934 | 935 | status_active: actif |
|
935 | 936 | status_registered: enregistrΓ© |
|
936 | 937 | status_locked: verrouillΓ© |
|
937 | 938 | |
|
938 | 939 | project_status_active: actif |
|
939 | 940 | project_status_closed: fermΓ© |
|
940 | 941 | project_status_archived: archivΓ© |
|
941 | 942 | |
|
942 | 943 | version_status_open: ouvert |
|
943 | 944 | version_status_locked: verrouillΓ© |
|
944 | 945 | version_status_closed: fermΓ© |
|
945 | 946 | |
|
946 | 947 | text_select_mail_notifications: Actions pour lesquelles une notification par e-mail est envoyΓ©e |
|
947 | 948 | text_regexp_info: ex. ^[A-Z0-9]+$ |
|
948 | 949 | text_min_max_length_info: 0 pour aucune restriction |
|
949 | 950 | text_project_destroy_confirmation: Γtes-vous sΓ»r de vouloir supprimer ce projet et toutes ses donnΓ©es ? |
|
950 | 951 | text_subprojects_destroy_warning: "Ses sous-projets : %{value} seront Γ©galement supprimΓ©s." |
|
951 | 952 | text_workflow_edit: SΓ©lectionner un tracker et un rΓ΄le pour Γ©diter le workflow |
|
952 | 953 | text_are_you_sure: Γtes-vous sΓ»r ? |
|
953 | 954 | text_tip_issue_begin_day: tΓ’che commenΓ§ant ce jour |
|
954 | 955 | text_tip_issue_end_day: tΓ’che finissant ce jour |
|
955 | 956 | text_tip_issue_begin_end_day: tΓ’che commenΓ§ant et finissant ce jour |
|
956 | 957 | 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Γ©.' |
|
957 | 958 | text_caracters_maximum: "%{count} caractères maximum." |
|
958 | 959 | text_caracters_minimum: "%{count} caractères minimum." |
|
959 | 960 | text_length_between: "Longueur comprise entre %{min} et %{max} caractères." |
|
960 | 961 | text_tracker_no_workflow: Aucun worflow n'est dΓ©fini pour ce tracker |
|
961 | 962 | text_unallowed_characters: Caractères non autorisés |
|
962 | 963 | text_comma_separated: Plusieurs valeurs possibles (sΓ©parΓ©es par des virgules). |
|
963 | 964 | text_line_separated: Plusieurs valeurs possibles (une valeur par ligne). |
|
964 | 965 | text_issues_ref_in_commit_messages: RΓ©fΓ©rencement et rΓ©solution des demandes dans les commentaires de commits |
|
965 | 966 | text_issue_added: "La demande %{id} a Γ©tΓ© soumise par %{author}." |
|
966 | 967 | text_issue_updated: "La demande %{id} a Γ©tΓ© mise Γ jour par %{author}." |
|
967 | 968 | text_wiki_destroy_confirmation: Etes-vous sΓ»r de vouloir supprimer ce wiki et tout son contenu ? |
|
968 | 969 | text_issue_category_destroy_question: "%{count} demandes sont affectΓ©es Γ cette catΓ©gorie. Que voulez-vous faire ?" |
|
969 | 970 | text_issue_category_destroy_assignments: N'affecter les demandes Γ aucune autre catΓ©gorie |
|
970 | 971 | text_issue_category_reassign_to: RΓ©affecter les demandes Γ cette catΓ©gorie |
|
971 | 972 | 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)." |
|
972 | 973 | 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Γ©." |
|
973 | 974 | text_load_default_configuration: Charger le paramΓ©trage par dΓ©faut |
|
974 | 975 | text_status_changed_by_changeset: "AppliquΓ© par commit %{value}." |
|
975 | 976 | text_time_logged_by_changeset: "AppliquΓ© par commit %{value}" |
|
976 | 977 | text_issues_destroy_confirmation: 'Γtes-vous sΓ»r de vouloir supprimer la ou les demandes(s) selectionnΓ©e(s) ?' |
|
977 | 978 | text_issues_destroy_descendants_confirmation: "Cela entrainera Γ©galement la suppression de %{count} sous-tΓ’che(s)." |
|
978 | 979 | text_select_project_modules: 'SΓ©lectionner les modules Γ activer pour ce projet :' |
|
979 | 980 | text_default_administrator_account_changed: Compte administrateur par dΓ©faut changΓ© |
|
980 | 981 | text_file_repository_writable: RΓ©pertoire de stockage des fichiers accessible en Γ©criture |
|
981 | 982 | text_plugin_assets_writable: RΓ©pertoire public des plugins accessible en Γ©criture |
|
982 | 983 | text_rmagick_available: Bibliothèque RMagick présente (optionnelle) |
|
983 | 984 | text_destroy_time_entries_question: "%{hours} heures ont Γ©tΓ© enregistrΓ©es sur les demandes Γ supprimer. Que voulez-vous faire ?" |
|
984 | 985 | text_destroy_time_entries: Supprimer les heures |
|
985 | 986 | text_assign_time_entries_to_project: Reporter les heures sur le projet |
|
986 | 987 | text_reassign_time_entries: 'Reporter les heures sur cette demande:' |
|
987 | 988 | text_user_wrote: "%{value} a Γ©crit :" |
|
988 | 989 | text_enumeration_destroy_question: "Cette valeur est affectΓ©e Γ %{count} objets." |
|
989 | 990 | text_enumeration_category_reassign_to: 'RΓ©affecter les objets Γ cette valeur:' |
|
990 | 991 | 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." |
|
991 | 992 | 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." |
|
992 | 993 | text_diff_truncated: '... Ce diffΓ©rentiel a Γ©tΓ© tronquΓ© car il excΓ¨de la taille maximale pouvant Γͺtre affichΓ©e.' |
|
993 | 994 | text_custom_field_possible_values_info: 'Une ligne par valeur' |
|
994 | 995 | text_wiki_page_destroy_question: "Cette page possède %{descendants} sous-page(s) et descendante(s). Que voulez-vous faire ?" |
|
995 | 996 | text_wiki_page_nullify_children: "Conserver les sous-pages en tant que pages racines" |
|
996 | 997 | text_wiki_page_destroy_children: "Supprimer les sous-pages et toutes leurs descedantes" |
|
997 | 998 | text_wiki_page_reassign_children: "RΓ©affecter les sous-pages Γ cette page" |
|
998 | 999 | 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 ?" |
|
999 | 1000 | text_warn_on_leaving_unsaved: "Cette page contient du texte non sauvegardΓ© qui sera perdu si vous quittez la page." |
|
1000 | 1001 | 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)" |
|
1001 | 1002 | text_issue_conflict_resolution_add_notes: "Ajouter mes notes et ignorer mes autres changements" |
|
1002 | 1003 | text_issue_conflict_resolution_cancel: "Annuler ma mise Γ jour et rΓ©afficher %{link}" |
|
1003 | 1004 | text_account_destroy_confirmation: "Γtes-vous sΓ»r de vouloir continuer ?\nVotre compte sera dΓ©finitivement supprimΓ©, sans aucune possibilitΓ© de le rΓ©activer." |
|
1004 | 1005 | 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." |
|
1005 | 1006 | text_project_closed: Ce projet est fermΓ© et accessible en lecture seule. |
|
1006 | 1007 | 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." |
|
1007 | 1008 | |
|
1008 | 1009 | default_role_manager: "Manager " |
|
1009 | 1010 | default_role_developer: "DΓ©veloppeur " |
|
1010 | 1011 | default_role_reporter: "Rapporteur " |
|
1011 | 1012 | default_tracker_bug: Anomalie |
|
1012 | 1013 | default_tracker_feature: Evolution |
|
1013 | 1014 | default_tracker_support: Assistance |
|
1014 | 1015 | default_issue_status_new: Nouveau |
|
1015 | 1016 | default_issue_status_in_progress: En cours |
|
1016 | 1017 | default_issue_status_resolved: RΓ©solu |
|
1017 | 1018 | default_issue_status_feedback: Commentaire |
|
1018 | 1019 | default_issue_status_closed: FermΓ© |
|
1019 | 1020 | default_issue_status_rejected: RejetΓ© |
|
1020 | 1021 | default_doc_category_user: Documentation utilisateur |
|
1021 | 1022 | default_doc_category_tech: Documentation technique |
|
1022 | 1023 | default_priority_low: Bas |
|
1023 | 1024 | default_priority_normal: Normal |
|
1024 | 1025 | default_priority_high: Haut |
|
1025 | 1026 | default_priority_urgent: Urgent |
|
1026 | 1027 | default_priority_immediate: ImmΓ©diat |
|
1027 | 1028 | default_activity_design: Conception |
|
1028 | 1029 | default_activity_development: DΓ©veloppement |
|
1029 | 1030 | |
|
1030 | 1031 | enumeration_issue_priorities: PrioritΓ©s des demandes |
|
1031 | 1032 | enumeration_doc_categories: CatΓ©gories des documents |
|
1032 | 1033 | enumeration_activities: ActivitΓ©s (suivi du temps) |
|
1033 | 1034 | label_greater_or_equal: ">=" |
|
1034 | 1035 | label_less_or_equal: "<=" |
|
1035 | 1036 | label_between: entre |
|
1036 | 1037 | label_view_all_revisions: Voir toutes les rΓ©visions |
|
1037 | 1038 | label_tag: Tag |
|
1038 | 1039 | label_branch: Branche |
|
1039 | 1040 | error_no_tracker_in_project: "Aucun tracker n'est associΓ© Γ ce projet. VΓ©rifier la configuration du projet." |
|
1040 | 1041 | error_no_default_issue_status: "Aucun statut de demande n'est dΓ©fini par dΓ©faut. VΓ©rifier votre configuration (Administration -> Statuts de demandes)." |
|
1041 | 1042 | text_journal_changed: "%{label} changΓ© de %{old} Γ %{new}" |
|
1042 | 1043 | text_journal_changed_no_detail: "%{label} mis Γ jour" |
|
1043 | 1044 | text_journal_set_to: "%{label} mis Γ %{value}" |
|
1044 | 1045 | text_journal_deleted: "%{label} %{old} supprimΓ©" |
|
1045 | 1046 | text_journal_added: "%{label} %{value} ajoutΓ©" |
|
1046 | 1047 | enumeration_system_activity: Activité système |
|
1047 | 1048 | label_board_sticky: Sticky |
|
1048 | 1049 | label_board_locked: VerrouillΓ© |
|
1049 | 1050 | error_unable_delete_issue_status: Impossible de supprimer le statut de demande |
|
1050 | 1051 | error_can_not_delete_custom_field: Impossible de supprimer le champ personnalisΓ© |
|
1051 | 1052 | error_unable_to_connect: Connexion impossible (%{value}) |
|
1052 | 1053 | error_can_not_remove_role: Ce rΓ΄le est utilisΓ© et ne peut pas Γͺtre supprimΓ©. |
|
1053 | 1054 | error_can_not_delete_tracker: Ce tracker contient des demandes et ne peut pas Γͺtre supprimΓ©. |
|
1054 | 1055 | field_principal: Principal |
|
1055 | 1056 | notice_failed_to_save_members: "Erreur lors de la sauvegarde des membres: %{errors}." |
|
1056 | 1057 | text_zoom_out: Zoom arrière |
|
1057 | 1058 | text_zoom_in: Zoom avant |
|
1058 | 1059 | notice_unable_delete_time_entry: Impossible de supprimer le temps passΓ©. |
|
1059 | 1060 | label_overall_spent_time: Temps passΓ© global |
|
1060 | 1061 | field_time_entries: Temps passΓ© |
|
1061 | 1062 | project_module_gantt: Gantt |
|
1062 | 1063 | project_module_calendar: Calendrier |
|
1063 | 1064 | button_edit_associated_wikipage: "Modifier la page wiki associΓ©e: %{page_title}" |
|
1064 | 1065 | field_text: Champ texte |
|
1065 | 1066 | label_user_mail_option_only_owner: Seulement pour ce que j'ai créé |
|
1066 | 1067 | setting_default_notification_option: Option de notification par dΓ©faut |
|
1067 | 1068 | label_user_mail_option_only_my_events: Seulement pour ce que je surveille |
|
1068 | 1069 | label_user_mail_option_only_assigned: Seulement pour ce qui m'est assignΓ© |
|
1069 | 1070 | label_user_mail_option_none: Aucune notification |
|
1070 | 1071 | field_member_of_group: Groupe de l'assignΓ© |
|
1071 | 1072 | field_assigned_to_role: RΓ΄le de l'assignΓ© |
|
1072 | 1073 | setting_emails_header: En-tΓͺte des emails |
|
1073 | 1074 | label_bulk_edit_selected_time_entries: Modifier les temps passΓ©s sΓ©lectionnΓ©s |
|
1074 | 1075 | text_time_entries_destroy_confirmation: "Etes-vous sΓ»r de vouloir supprimer les temps passΓ©s sΓ©lectionnΓ©s ?" |
|
1075 | 1076 | field_scm_path_encoding: Encodage des chemins |
|
1076 | 1077 | text_scm_path_encoding_note: "DΓ©faut : UTF-8" |
|
1077 | 1078 | field_path_to_repository: Chemin du dΓ©pΓ΄t |
|
1078 | 1079 | field_root_directory: RΓ©pertoire racine |
|
1079 | 1080 | field_cvs_module: Module |
|
1080 | 1081 | field_cvsroot: CVSROOT |
|
1081 | 1082 | text_mercurial_repository_note: "DΓ©pΓ΄t local (exemples : /hgrepo, c:\\hgrepo)" |
|
1082 | 1083 | text_scm_command: Commande |
|
1083 | 1084 | text_scm_command_version: Version |
|
1084 | 1085 | label_git_report_last_commit: Afficher le dernier commit des fichiers et rΓ©pertoires |
|
1085 | 1086 | text_scm_config: Vous pouvez configurer les commandes des SCM dans config/configuration.yml. Redémarrer l'application après modification. |
|
1086 | 1087 | text_scm_command_not_available: Ce SCM n'est pas disponible. Vérifier les paramètres dans la section administration. |
|
1087 | 1088 | label_diff: diff |
|
1088 | 1089 | text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo) |
|
1089 | 1090 | description_query_sort_criteria_direction: Ordre de tri |
|
1090 | 1091 | description_project_scope: Périmètre de recherche |
|
1091 | 1092 | description_filter: Filtre |
|
1092 | 1093 | description_user_mail_notification: Option de notification |
|
1093 | 1094 | description_date_from: Date de dΓ©but |
|
1094 | 1095 | description_message_content: Contenu du message |
|
1095 | 1096 | description_available_columns: Colonnes disponibles |
|
1096 | 1097 | description_all_columns: Toutes les colonnes |
|
1097 | 1098 | description_date_range_interval: Choisir une pΓ©riode |
|
1098 | 1099 | description_issue_category_reassign: Choisir une catΓ©gorie |
|
1099 | 1100 | description_search: Champ de recherche |
|
1100 | 1101 | description_notes: Notes |
|
1101 | 1102 | description_date_range_list: Choisir une pΓ©riode prΓ©dΓ©finie |
|
1102 | 1103 | description_choose_project: Projets |
|
1103 | 1104 | description_date_to: Date de fin |
|
1104 | 1105 | description_query_sort_criteria_attribute: Critère de tri |
|
1105 | 1106 | description_wiki_subpages_reassign: Choisir une nouvelle page parent |
|
1106 | 1107 | description_selected_columns: Colonnes sΓ©lectionnΓ©es |
|
1107 | 1108 | label_parent_revision: Parent |
|
1108 | 1109 | label_child_revision: Enfant |
|
1109 | 1110 | error_scm_annotate_big_text_file: Cette entrΓ©e ne peut pas Γͺtre annotΓ©e car elle excΓ¨de la taille maximale. |
|
1110 | 1111 | setting_repositories_encodings: Encodages des fichiers et des dΓ©pΓ΄ts |
|
1111 | 1112 | label_search_for_watchers: Rechercher des observateurs |
|
1112 | 1113 | 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Γ©.' |
@@ -1,232 +1,234 | |||
|
1 | 1 | # Redmine - project management software |
|
2 | 2 | # Copyright (C) 2006-2013 Jean-Philippe Lang |
|
3 | 3 | # |
|
4 | 4 | # This program is free software; you can redistribute it and/or |
|
5 | 5 | # modify it under the terms of the GNU General Public License |
|
6 | 6 | # as published by the Free Software Foundation; either version 2 |
|
7 | 7 | # of the License, or (at your option) any later version. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU General Public License |
|
15 | 15 | # along with this program; if not, write to the Free Software |
|
16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | 17 | |
|
18 | 18 | |
|
19 | 19 | # DO NOT MODIFY THIS FILE !!! |
|
20 | 20 | # Settings can be defined through the application in Admin -> Settings |
|
21 | 21 | |
|
22 | 22 | app_title: |
|
23 | 23 | default: Redmine |
|
24 | 24 | app_subtitle: |
|
25 | 25 | default: Project management |
|
26 | 26 | welcome_text: |
|
27 | 27 | default: |
|
28 | 28 | login_required: |
|
29 | 29 | default: 0 |
|
30 | 30 | self_registration: |
|
31 | 31 | default: '2' |
|
32 | 32 | lost_password: |
|
33 | 33 | default: 1 |
|
34 | 34 | unsubscribe: |
|
35 | 35 | default: 1 |
|
36 | 36 | password_min_length: |
|
37 | 37 | format: int |
|
38 | 38 | default: 8 |
|
39 | 39 | # Maximum lifetime of user sessions in minutes |
|
40 | 40 | session_lifetime: |
|
41 | 41 | format: int |
|
42 | 42 | default: 0 |
|
43 | 43 | # User session timeout in minutes |
|
44 | 44 | session_timeout: |
|
45 | 45 | format: int |
|
46 | 46 | default: 0 |
|
47 | 47 | attachment_max_size: |
|
48 | 48 | format: int |
|
49 | 49 | default: 5120 |
|
50 | 50 | issues_export_limit: |
|
51 | 51 | format: int |
|
52 | 52 | default: 500 |
|
53 | 53 | activity_days_default: |
|
54 | 54 | format: int |
|
55 | 55 | default: 30 |
|
56 | 56 | per_page_options: |
|
57 | 57 | default: '25,50,100' |
|
58 | 58 | mail_from: |
|
59 | 59 | default: redmine@example.net |
|
60 | 60 | bcc_recipients: |
|
61 | 61 | default: 1 |
|
62 | 62 | plain_text_mail: |
|
63 | 63 | default: 0 |
|
64 | 64 | text_formatting: |
|
65 | 65 | default: textile |
|
66 | 66 | cache_formatted_text: |
|
67 | 67 | default: 0 |
|
68 | 68 | wiki_compression: |
|
69 | 69 | default: "" |
|
70 | 70 | default_language: |
|
71 | 71 | default: en |
|
72 | 72 | host_name: |
|
73 | 73 | default: localhost:3000 |
|
74 | 74 | protocol: |
|
75 | 75 | default: http |
|
76 | 76 | feeds_limit: |
|
77 | 77 | format: int |
|
78 | 78 | default: 15 |
|
79 | 79 | gantt_items_limit: |
|
80 | 80 | format: int |
|
81 | 81 | default: 500 |
|
82 | 82 | # Maximum size of files that can be displayed |
|
83 | 83 | # inline through the file viewer (in KB) |
|
84 | 84 | file_max_size_displayed: |
|
85 | 85 | format: int |
|
86 | 86 | default: 512 |
|
87 | 87 | diff_max_lines_displayed: |
|
88 | 88 | format: int |
|
89 | 89 | default: 1500 |
|
90 | 90 | enabled_scm: |
|
91 | 91 | serialized: true |
|
92 | 92 | default: |
|
93 | 93 | - Subversion |
|
94 | 94 | - Darcs |
|
95 | 95 | - Mercurial |
|
96 | 96 | - Cvs |
|
97 | 97 | - Bazaar |
|
98 | 98 | - Git |
|
99 | 99 | autofetch_changesets: |
|
100 | 100 | default: 1 |
|
101 | 101 | sys_api_enabled: |
|
102 | 102 | default: 0 |
|
103 | 103 | sys_api_key: |
|
104 | 104 | default: '' |
|
105 | 105 | commit_cross_project_ref: |
|
106 | 106 | default: 0 |
|
107 | 107 | commit_ref_keywords: |
|
108 | 108 | default: 'refs,references,IssueID' |
|
109 | 109 | commit_fix_keywords: |
|
110 | 110 | default: 'fixes,closes' |
|
111 | 111 | commit_fix_status_id: |
|
112 | 112 | format: int |
|
113 | 113 | default: 0 |
|
114 | 114 | commit_fix_done_ratio: |
|
115 | 115 | default: 100 |
|
116 | 116 | commit_logtime_enabled: |
|
117 | 117 | default: 0 |
|
118 | 118 | commit_logtime_activity_id: |
|
119 | 119 | format: int |
|
120 | 120 | default: 0 |
|
121 | 121 | # autologin duration in days |
|
122 | 122 | # 0 means autologin is disabled |
|
123 | 123 | autologin: |
|
124 | 124 | format: int |
|
125 | 125 | default: 0 |
|
126 | 126 | # date format |
|
127 | 127 | date_format: |
|
128 | 128 | default: '' |
|
129 | 129 | time_format: |
|
130 | 130 | default: '' |
|
131 | 131 | user_format: |
|
132 | 132 | default: :firstname_lastname |
|
133 | 133 | format: symbol |
|
134 | 134 | cross_project_issue_relations: |
|
135 | 135 | default: 0 |
|
136 | 136 | # Enables subtasks to be in other projects |
|
137 | 137 | cross_project_subtasks: |
|
138 | 138 | default: 'tree' |
|
139 | 139 | issue_group_assignment: |
|
140 | 140 | default: 0 |
|
141 | 141 | default_issue_start_date_to_creation_date: |
|
142 | 142 | default: 1 |
|
143 | 143 | notified_events: |
|
144 | 144 | serialized: true |
|
145 | 145 | default: |
|
146 | 146 | - issue_added |
|
147 | 147 | - issue_updated |
|
148 | 148 | mail_handler_body_delimiters: |
|
149 | 149 | default: '' |
|
150 | mail_handler_excluded_filenames: | |
|
151 | default: '' | |
|
150 | 152 | mail_handler_api_enabled: |
|
151 | 153 | default: 0 |
|
152 | 154 | mail_handler_api_key: |
|
153 | 155 | default: |
|
154 | 156 | issue_list_default_columns: |
|
155 | 157 | serialized: true |
|
156 | 158 | default: |
|
157 | 159 | - tracker |
|
158 | 160 | - status |
|
159 | 161 | - priority |
|
160 | 162 | - subject |
|
161 | 163 | - assigned_to |
|
162 | 164 | - updated_on |
|
163 | 165 | display_subprojects_issues: |
|
164 | 166 | default: 1 |
|
165 | 167 | issue_done_ratio: |
|
166 | 168 | default: 'issue_field' |
|
167 | 169 | default_projects_public: |
|
168 | 170 | default: 1 |
|
169 | 171 | default_projects_modules: |
|
170 | 172 | serialized: true |
|
171 | 173 | default: |
|
172 | 174 | - issue_tracking |
|
173 | 175 | - time_tracking |
|
174 | 176 | - news |
|
175 | 177 | - documents |
|
176 | 178 | - files |
|
177 | 179 | - wiki |
|
178 | 180 | - repository |
|
179 | 181 | - boards |
|
180 | 182 | - calendar |
|
181 | 183 | - gantt |
|
182 | 184 | default_projects_tracker_ids: |
|
183 | 185 | serialized: true |
|
184 | 186 | default: |
|
185 | 187 | # Role given to a non-admin user who creates a project |
|
186 | 188 | new_project_user_role_id: |
|
187 | 189 | format: int |
|
188 | 190 | default: '' |
|
189 | 191 | sequential_project_identifiers: |
|
190 | 192 | default: 0 |
|
191 | 193 | # encodings used to convert repository files content to UTF-8 |
|
192 | 194 | # multiple values accepted, comma separated |
|
193 | 195 | repositories_encodings: |
|
194 | 196 | default: '' |
|
195 | 197 | # encoding used to convert commit logs to UTF-8 |
|
196 | 198 | commit_logs_encoding: |
|
197 | 199 | default: 'UTF-8' |
|
198 | 200 | repository_log_display_limit: |
|
199 | 201 | format: int |
|
200 | 202 | default: 100 |
|
201 | 203 | ui_theme: |
|
202 | 204 | default: '' |
|
203 | 205 | emails_footer: |
|
204 | 206 | default: |- |
|
205 | 207 | You have received this notification because you have either subscribed to it, or are involved in it. |
|
206 | 208 | To change your notification preferences, please click here: http://hostname/my/account |
|
207 | 209 | gravatar_enabled: |
|
208 | 210 | default: 0 |
|
209 | 211 | openid: |
|
210 | 212 | default: 0 |
|
211 | 213 | gravatar_default: |
|
212 | 214 | default: '' |
|
213 | 215 | start_of_week: |
|
214 | 216 | default: '' |
|
215 | 217 | rest_api_enabled: |
|
216 | 218 | default: 0 |
|
217 | 219 | jsonp_enabled: |
|
218 | 220 | default: 0 |
|
219 | 221 | default_notification_option: |
|
220 | 222 | default: 'only_my_events' |
|
221 | 223 | emails_header: |
|
222 | 224 | default: '' |
|
223 | 225 | thumbnails_enabled: |
|
224 | 226 | default: 0 |
|
225 | 227 | thumbnails_size: |
|
226 | 228 | format: int |
|
227 | 229 | default: 100 |
|
228 | 230 | non_working_week_days: |
|
229 | 231 | serialized: true |
|
230 | 232 | default: |
|
231 | 233 | - '6' |
|
232 | 234 | - '7' |
@@ -1,843 +1,861 | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | # |
|
3 | 3 | # Redmine - project management software |
|
4 | 4 | # Copyright (C) 2006-2013 Jean-Philippe Lang |
|
5 | 5 | # |
|
6 | 6 | # This program is free software; you can redistribute it and/or |
|
7 | 7 | # modify it under the terms of the GNU General Public License |
|
8 | 8 | # as published by the Free Software Foundation; either version 2 |
|
9 | 9 | # of the License, or (at your option) any later version. |
|
10 | 10 | # |
|
11 | 11 | # This program is distributed in the hope that it will be useful, |
|
12 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 | 14 | # GNU General Public License for more details. |
|
15 | 15 | # |
|
16 | 16 | # You should have received a copy of the GNU General Public License |
|
17 | 17 | # along with this program; if not, write to the Free Software |
|
18 | 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
19 | 19 | |
|
20 | 20 | require File.expand_path('../../test_helper', __FILE__) |
|
21 | 21 | |
|
22 | 22 | class MailHandlerTest < ActiveSupport::TestCase |
|
23 | 23 | fixtures :users, :projects, :enabled_modules, :roles, |
|
24 | 24 | :members, :member_roles, :users, |
|
25 | 25 | :issues, :issue_statuses, |
|
26 | 26 | :workflows, :trackers, :projects_trackers, |
|
27 | 27 | :versions, :enumerations, :issue_categories, |
|
28 | 28 | :custom_fields, :custom_fields_trackers, :custom_fields_projects, |
|
29 | 29 | :boards, :messages |
|
30 | 30 | |
|
31 | 31 | FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' |
|
32 | 32 | |
|
33 | 33 | def setup |
|
34 | 34 | ActionMailer::Base.deliveries.clear |
|
35 | 35 | Setting.notified_events = Redmine::Notifiable.all.collect(&:name) |
|
36 | 36 | end |
|
37 | 37 | |
|
38 | 38 | def teardown |
|
39 | 39 | Setting.clear_cache |
|
40 | 40 | end |
|
41 | 41 | |
|
42 | 42 | def test_add_issue |
|
43 | 43 | ActionMailer::Base.deliveries.clear |
|
44 | 44 | # This email contains: 'Project: onlinestore' |
|
45 | 45 | issue = submit_email('ticket_on_given_project.eml') |
|
46 | 46 | assert issue.is_a?(Issue) |
|
47 | 47 | assert !issue.new_record? |
|
48 | 48 | issue.reload |
|
49 | 49 | assert_equal Project.find(2), issue.project |
|
50 | 50 | assert_equal issue.project.trackers.first, issue.tracker |
|
51 | 51 | assert_equal 'New ticket on a given project', issue.subject |
|
52 | 52 | assert_equal User.find_by_login('jsmith'), issue.author |
|
53 | 53 | assert_equal IssueStatus.find_by_name('Resolved'), issue.status |
|
54 | 54 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
55 | 55 | assert_equal '2010-01-01', issue.start_date.to_s |
|
56 | 56 | assert_equal '2010-12-31', issue.due_date.to_s |
|
57 | 57 | assert_equal User.find_by_login('jsmith'), issue.assigned_to |
|
58 | 58 | assert_equal Version.find_by_name('Alpha'), issue.fixed_version |
|
59 | 59 | assert_equal 2.5, issue.estimated_hours |
|
60 | 60 | assert_equal 30, issue.done_ratio |
|
61 | 61 | assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt] |
|
62 | 62 | # keywords should be removed from the email body |
|
63 | 63 | assert !issue.description.match(/^Project:/i) |
|
64 | 64 | assert !issue.description.match(/^Status:/i) |
|
65 | 65 | assert !issue.description.match(/^Start Date:/i) |
|
66 | 66 | # Email notification should be sent |
|
67 | 67 | mail = ActionMailer::Base.deliveries.last |
|
68 | 68 | assert_not_nil mail |
|
69 | 69 | assert mail.subject.include?('New ticket on a given project') |
|
70 | 70 | end |
|
71 | 71 | |
|
72 | 72 | def test_add_issue_with_default_tracker |
|
73 | 73 | # This email contains: 'Project: onlinestore' |
|
74 | 74 | issue = submit_email( |
|
75 | 75 | 'ticket_on_given_project.eml', |
|
76 | 76 | :issue => {:tracker => 'Support request'} |
|
77 | 77 | ) |
|
78 | 78 | assert issue.is_a?(Issue) |
|
79 | 79 | assert !issue.new_record? |
|
80 | 80 | issue.reload |
|
81 | 81 | assert_equal 'Support request', issue.tracker.name |
|
82 | 82 | end |
|
83 | 83 | |
|
84 | 84 | def test_add_issue_with_status |
|
85 | 85 | # This email contains: 'Project: onlinestore' and 'Status: Resolved' |
|
86 | 86 | issue = submit_email('ticket_on_given_project.eml') |
|
87 | 87 | assert issue.is_a?(Issue) |
|
88 | 88 | assert !issue.new_record? |
|
89 | 89 | issue.reload |
|
90 | 90 | assert_equal Project.find(2), issue.project |
|
91 | 91 | assert_equal IssueStatus.find_by_name("Resolved"), issue.status |
|
92 | 92 | end |
|
93 | 93 | |
|
94 | 94 | def test_add_issue_with_attributes_override |
|
95 | 95 | issue = submit_email( |
|
96 | 96 | 'ticket_with_attributes.eml', |
|
97 | 97 | :allow_override => 'tracker,category,priority' |
|
98 | 98 | ) |
|
99 | 99 | assert issue.is_a?(Issue) |
|
100 | 100 | assert !issue.new_record? |
|
101 | 101 | issue.reload |
|
102 | 102 | assert_equal 'New ticket on a given project', issue.subject |
|
103 | 103 | assert_equal User.find_by_login('jsmith'), issue.author |
|
104 | 104 | assert_equal Project.find(2), issue.project |
|
105 | 105 | assert_equal 'Feature request', issue.tracker.to_s |
|
106 | 106 | assert_equal 'Stock management', issue.category.to_s |
|
107 | 107 | assert_equal 'Urgent', issue.priority.to_s |
|
108 | 108 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
109 | 109 | end |
|
110 | 110 | |
|
111 | 111 | def test_add_issue_with_group_assignment |
|
112 | 112 | with_settings :issue_group_assignment => '1' do |
|
113 | 113 | issue = submit_email('ticket_on_given_project.eml') do |email| |
|
114 | 114 | email.gsub!('Assigned to: John Smith', 'Assigned to: B Team') |
|
115 | 115 | end |
|
116 | 116 | assert issue.is_a?(Issue) |
|
117 | 117 | assert !issue.new_record? |
|
118 | 118 | issue.reload |
|
119 | 119 | assert_equal Group.find(11), issue.assigned_to |
|
120 | 120 | end |
|
121 | 121 | end |
|
122 | 122 | |
|
123 | 123 | def test_add_issue_with_partial_attributes_override |
|
124 | 124 | issue = submit_email( |
|
125 | 125 | 'ticket_with_attributes.eml', |
|
126 | 126 | :issue => {:priority => 'High'}, |
|
127 | 127 | :allow_override => ['tracker'] |
|
128 | 128 | ) |
|
129 | 129 | assert issue.is_a?(Issue) |
|
130 | 130 | assert !issue.new_record? |
|
131 | 131 | issue.reload |
|
132 | 132 | assert_equal 'New ticket on a given project', issue.subject |
|
133 | 133 | assert_equal User.find_by_login('jsmith'), issue.author |
|
134 | 134 | assert_equal Project.find(2), issue.project |
|
135 | 135 | assert_equal 'Feature request', issue.tracker.to_s |
|
136 | 136 | assert_nil issue.category |
|
137 | 137 | assert_equal 'High', issue.priority.to_s |
|
138 | 138 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
139 | 139 | end |
|
140 | 140 | |
|
141 | 141 | def test_add_issue_with_spaces_between_attribute_and_separator |
|
142 | 142 | issue = submit_email( |
|
143 | 143 | 'ticket_with_spaces_between_attribute_and_separator.eml', |
|
144 | 144 | :allow_override => 'tracker,category,priority' |
|
145 | 145 | ) |
|
146 | 146 | assert issue.is_a?(Issue) |
|
147 | 147 | assert !issue.new_record? |
|
148 | 148 | issue.reload |
|
149 | 149 | assert_equal 'New ticket on a given project', issue.subject |
|
150 | 150 | assert_equal User.find_by_login('jsmith'), issue.author |
|
151 | 151 | assert_equal Project.find(2), issue.project |
|
152 | 152 | assert_equal 'Feature request', issue.tracker.to_s |
|
153 | 153 | assert_equal 'Stock management', issue.category.to_s |
|
154 | 154 | assert_equal 'Urgent', issue.priority.to_s |
|
155 | 155 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
156 | 156 | end |
|
157 | 157 | |
|
158 | 158 | def test_add_issue_with_attachment_to_specific_project |
|
159 | 159 | issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'}) |
|
160 | 160 | assert issue.is_a?(Issue) |
|
161 | 161 | assert !issue.new_record? |
|
162 | 162 | issue.reload |
|
163 | 163 | assert_equal 'Ticket created by email with attachment', issue.subject |
|
164 | 164 | assert_equal User.find_by_login('jsmith'), issue.author |
|
165 | 165 | assert_equal Project.find(2), issue.project |
|
166 | 166 | assert_equal 'This is a new ticket with attachments', issue.description |
|
167 | 167 | # Attachment properties |
|
168 | 168 | assert_equal 1, issue.attachments.size |
|
169 | 169 | assert_equal 'Paella.jpg', issue.attachments.first.filename |
|
170 | 170 | assert_equal 'image/jpeg', issue.attachments.first.content_type |
|
171 | 171 | assert_equal 10790, issue.attachments.first.filesize |
|
172 | 172 | end |
|
173 | 173 | |
|
174 | 174 | def test_add_issue_with_custom_fields |
|
175 | 175 | issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'}) |
|
176 | 176 | assert issue.is_a?(Issue) |
|
177 | 177 | assert !issue.new_record? |
|
178 | 178 | issue.reload |
|
179 | 179 | assert_equal 'New ticket with custom field values', issue.subject |
|
180 | 180 | assert_equal 'PostgreSQL', issue.custom_field_value(1) |
|
181 | 181 | assert_equal 'Value for a custom field', issue.custom_field_value(2) |
|
182 | 182 | assert !issue.description.match(/^searchable field:/i) |
|
183 | 183 | end |
|
184 | 184 | |
|
185 | 185 | def test_add_issue_with_version_custom_fields |
|
186 | 186 | field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3]) |
|
187 | 187 | |
|
188 | 188 | issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'ecookbook'}) do |email| |
|
189 | 189 | email << "Affected version: 1.0\n" |
|
190 | 190 | end |
|
191 | 191 | assert issue.is_a?(Issue) |
|
192 | 192 | assert !issue.new_record? |
|
193 | 193 | issue.reload |
|
194 | 194 | assert_equal '2', issue.custom_field_value(field) |
|
195 | 195 | end |
|
196 | 196 | |
|
197 | 197 | def test_add_issue_should_match_assignee_on_display_name |
|
198 | 198 | user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz') |
|
199 | 199 | User.add_to_project(user, Project.find(2)) |
|
200 | 200 | issue = submit_email('ticket_on_given_project.eml') do |email| |
|
201 | 201 | email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz') |
|
202 | 202 | end |
|
203 | 203 | assert issue.is_a?(Issue) |
|
204 | 204 | assert_equal user, issue.assigned_to |
|
205 | 205 | end |
|
206 | 206 | |
|
207 | 207 | def test_add_issue_with_cc |
|
208 | 208 | issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'}) |
|
209 | 209 | assert issue.is_a?(Issue) |
|
210 | 210 | assert !issue.new_record? |
|
211 | 211 | issue.reload |
|
212 | 212 | assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo')) |
|
213 | 213 | assert_equal 1, issue.watcher_user_ids.size |
|
214 | 214 | end |
|
215 | 215 | |
|
216 | 216 | def test_add_issue_by_unknown_user |
|
217 | 217 | assert_no_difference 'User.count' do |
|
218 | 218 | assert_equal false, |
|
219 | 219 | submit_email( |
|
220 | 220 | 'ticket_by_unknown_user.eml', |
|
221 | 221 | :issue => {:project => 'ecookbook'} |
|
222 | 222 | ) |
|
223 | 223 | end |
|
224 | 224 | end |
|
225 | 225 | |
|
226 | 226 | def test_add_issue_by_anonymous_user |
|
227 | 227 | Role.anonymous.add_permission!(:add_issues) |
|
228 | 228 | assert_no_difference 'User.count' do |
|
229 | 229 | issue = submit_email( |
|
230 | 230 | 'ticket_by_unknown_user.eml', |
|
231 | 231 | :issue => {:project => 'ecookbook'}, |
|
232 | 232 | :unknown_user => 'accept' |
|
233 | 233 | ) |
|
234 | 234 | assert issue.is_a?(Issue) |
|
235 | 235 | assert issue.author.anonymous? |
|
236 | 236 | end |
|
237 | 237 | end |
|
238 | 238 | |
|
239 | 239 | def test_add_issue_by_anonymous_user_with_no_from_address |
|
240 | 240 | Role.anonymous.add_permission!(:add_issues) |
|
241 | 241 | assert_no_difference 'User.count' do |
|
242 | 242 | issue = submit_email( |
|
243 | 243 | 'ticket_by_empty_user.eml', |
|
244 | 244 | :issue => {:project => 'ecookbook'}, |
|
245 | 245 | :unknown_user => 'accept' |
|
246 | 246 | ) |
|
247 | 247 | assert issue.is_a?(Issue) |
|
248 | 248 | assert issue.author.anonymous? |
|
249 | 249 | end |
|
250 | 250 | end |
|
251 | 251 | |
|
252 | 252 | def test_add_issue_by_anonymous_user_on_private_project |
|
253 | 253 | Role.anonymous.add_permission!(:add_issues) |
|
254 | 254 | assert_no_difference 'User.count' do |
|
255 | 255 | assert_no_difference 'Issue.count' do |
|
256 | 256 | assert_equal false, |
|
257 | 257 | submit_email( |
|
258 | 258 | 'ticket_by_unknown_user.eml', |
|
259 | 259 | :issue => {:project => 'onlinestore'}, |
|
260 | 260 | :unknown_user => 'accept' |
|
261 | 261 | ) |
|
262 | 262 | end |
|
263 | 263 | end |
|
264 | 264 | end |
|
265 | 265 | |
|
266 | 266 | def test_add_issue_by_anonymous_user_on_private_project_without_permission_check |
|
267 | 267 | assert_no_difference 'User.count' do |
|
268 | 268 | assert_difference 'Issue.count' do |
|
269 | 269 | issue = submit_email( |
|
270 | 270 | 'ticket_by_unknown_user.eml', |
|
271 | 271 | :issue => {:project => 'onlinestore'}, |
|
272 | 272 | :no_permission_check => '1', |
|
273 | 273 | :unknown_user => 'accept' |
|
274 | 274 | ) |
|
275 | 275 | assert issue.is_a?(Issue) |
|
276 | 276 | assert issue.author.anonymous? |
|
277 | 277 | assert !issue.project.is_public? |
|
278 | 278 | assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt] |
|
279 | 279 | end |
|
280 | 280 | end |
|
281 | 281 | end |
|
282 | 282 | |
|
283 | 283 | def test_add_issue_by_created_user |
|
284 | 284 | Setting.default_language = 'en' |
|
285 | 285 | assert_difference 'User.count' do |
|
286 | 286 | issue = submit_email( |
|
287 | 287 | 'ticket_by_unknown_user.eml', |
|
288 | 288 | :issue => {:project => 'ecookbook'}, |
|
289 | 289 | :unknown_user => 'create' |
|
290 | 290 | ) |
|
291 | 291 | assert issue.is_a?(Issue) |
|
292 | 292 | assert issue.author.active? |
|
293 | 293 | assert_equal 'john.doe@somenet.foo', issue.author.mail |
|
294 | 294 | assert_equal 'John', issue.author.firstname |
|
295 | 295 | assert_equal 'Doe', issue.author.lastname |
|
296 | 296 | |
|
297 | 297 | # account information |
|
298 | 298 | email = ActionMailer::Base.deliveries.first |
|
299 | 299 | assert_not_nil email |
|
300 | 300 | assert email.subject.include?('account activation') |
|
301 | 301 | login = mail_body(email).match(/\* Login: (.*)$/)[1].strip |
|
302 | 302 | password = mail_body(email).match(/\* Password: (.*)$/)[1].strip |
|
303 | 303 | assert_equal issue.author, User.try_to_login(login, password) |
|
304 | 304 | end |
|
305 | 305 | end |
|
306 | 306 | |
|
307 | 307 | def test_created_user_should_be_added_to_groups |
|
308 | 308 | group1 = Group.generate! |
|
309 | 309 | group2 = Group.generate! |
|
310 | 310 | |
|
311 | 311 | assert_difference 'User.count' do |
|
312 | 312 | submit_email( |
|
313 | 313 | 'ticket_by_unknown_user.eml', |
|
314 | 314 | :issue => {:project => 'ecookbook'}, |
|
315 | 315 | :unknown_user => 'create', |
|
316 | 316 | :default_group => "#{group1.name},#{group2.name}" |
|
317 | 317 | ) |
|
318 | 318 | end |
|
319 | 319 | user = User.order('id DESC').first |
|
320 | 320 | assert_same_elements [group1, group2], user.groups |
|
321 | 321 | end |
|
322 | 322 | |
|
323 | 323 | def test_created_user_should_not_receive_account_information_with_no_account_info_option |
|
324 | 324 | assert_difference 'User.count' do |
|
325 | 325 | submit_email( |
|
326 | 326 | 'ticket_by_unknown_user.eml', |
|
327 | 327 | :issue => {:project => 'ecookbook'}, |
|
328 | 328 | :unknown_user => 'create', |
|
329 | 329 | :no_account_notice => '1' |
|
330 | 330 | ) |
|
331 | 331 | end |
|
332 | 332 | |
|
333 | 333 | # only 1 email for the new issue notification |
|
334 | 334 | assert_equal 1, ActionMailer::Base.deliveries.size |
|
335 | 335 | email = ActionMailer::Base.deliveries.first |
|
336 | 336 | assert_include 'Ticket by unknown user', email.subject |
|
337 | 337 | end |
|
338 | 338 | |
|
339 | 339 | def test_created_user_should_have_mail_notification_to_none_with_no_notification_option |
|
340 | 340 | assert_difference 'User.count' do |
|
341 | 341 | submit_email( |
|
342 | 342 | 'ticket_by_unknown_user.eml', |
|
343 | 343 | :issue => {:project => 'ecookbook'}, |
|
344 | 344 | :unknown_user => 'create', |
|
345 | 345 | :no_notification => '1' |
|
346 | 346 | ) |
|
347 | 347 | end |
|
348 | 348 | user = User.order('id DESC').first |
|
349 | 349 | assert_equal 'none', user.mail_notification |
|
350 | 350 | end |
|
351 | 351 | |
|
352 | 352 | def test_add_issue_without_from_header |
|
353 | 353 | Role.anonymous.add_permission!(:add_issues) |
|
354 | 354 | assert_equal false, submit_email('ticket_without_from_header.eml') |
|
355 | 355 | end |
|
356 | 356 | |
|
357 | 357 | def test_add_issue_with_invalid_attributes |
|
358 | 358 | issue = submit_email( |
|
359 | 359 | 'ticket_with_invalid_attributes.eml', |
|
360 | 360 | :allow_override => 'tracker,category,priority' |
|
361 | 361 | ) |
|
362 | 362 | assert issue.is_a?(Issue) |
|
363 | 363 | assert !issue.new_record? |
|
364 | 364 | issue.reload |
|
365 | 365 | assert_nil issue.assigned_to |
|
366 | 366 | assert_nil issue.start_date |
|
367 | 367 | assert_nil issue.due_date |
|
368 | 368 | assert_equal 0, issue.done_ratio |
|
369 | 369 | assert_equal 'Normal', issue.priority.to_s |
|
370 | 370 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
371 | 371 | end |
|
372 | 372 | |
|
373 | 373 | def test_add_issue_with_invalid_project_should_be_assigned_to_default_project |
|
374 | 374 | issue = submit_email('ticket_on_given_project.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'project') do |email| |
|
375 | 375 | email.gsub!(/^Project:.+$/, 'Project: invalid') |
|
376 | 376 | end |
|
377 | 377 | assert issue.is_a?(Issue) |
|
378 | 378 | assert !issue.new_record? |
|
379 | 379 | assert_equal 'ecookbook', issue.project.identifier |
|
380 | 380 | end |
|
381 | 381 | |
|
382 | 382 | def test_add_issue_with_localized_attributes |
|
383 | 383 | User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr' |
|
384 | 384 | issue = submit_email( |
|
385 | 385 | 'ticket_with_localized_attributes.eml', |
|
386 | 386 | :allow_override => 'tracker,category,priority' |
|
387 | 387 | ) |
|
388 | 388 | assert issue.is_a?(Issue) |
|
389 | 389 | assert !issue.new_record? |
|
390 | 390 | issue.reload |
|
391 | 391 | assert_equal 'New ticket on a given project', issue.subject |
|
392 | 392 | assert_equal User.find_by_login('jsmith'), issue.author |
|
393 | 393 | assert_equal Project.find(2), issue.project |
|
394 | 394 | assert_equal 'Feature request', issue.tracker.to_s |
|
395 | 395 | assert_equal 'Stock management', issue.category.to_s |
|
396 | 396 | assert_equal 'Urgent', issue.priority.to_s |
|
397 | 397 | assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.') |
|
398 | 398 | end |
|
399 | 399 | |
|
400 | 400 | def test_add_issue_with_japanese_keywords |
|
401 | 401 | ja_dev = "\xe9\x96\x8b\xe7\x99\xba" |
|
402 | 402 | ja_dev.force_encoding('UTF-8') if ja_dev.respond_to?(:force_encoding) |
|
403 | 403 | tracker = Tracker.create!(:name => ja_dev) |
|
404 | 404 | Project.find(1).trackers << tracker |
|
405 | 405 | issue = submit_email( |
|
406 | 406 | 'japanese_keywords_iso_2022_jp.eml', |
|
407 | 407 | :issue => {:project => 'ecookbook'}, |
|
408 | 408 | :allow_override => 'tracker' |
|
409 | 409 | ) |
|
410 | 410 | assert_kind_of Issue, issue |
|
411 | 411 | assert_equal tracker, issue.tracker |
|
412 | 412 | end |
|
413 | 413 | |
|
414 | 414 | def test_add_issue_from_apple_mail |
|
415 | 415 | issue = submit_email( |
|
416 | 416 | 'apple_mail_with_attachment.eml', |
|
417 | 417 | :issue => {:project => 'ecookbook'} |
|
418 | 418 | ) |
|
419 | 419 | assert_kind_of Issue, issue |
|
420 | 420 | assert_equal 1, issue.attachments.size |
|
421 | 421 | |
|
422 | 422 | attachment = issue.attachments.first |
|
423 | 423 | assert_equal 'paella.jpg', attachment.filename |
|
424 | 424 | assert_equal 10790, attachment.filesize |
|
425 | 425 | assert File.exist?(attachment.diskfile) |
|
426 | 426 | assert_equal 10790, File.size(attachment.diskfile) |
|
427 | 427 | assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest |
|
428 | 428 | end |
|
429 | 429 | |
|
430 | 430 | def test_thunderbird_with_attachment_ja |
|
431 | 431 | issue = submit_email( |
|
432 | 432 | 'thunderbird_with_attachment_ja.eml', |
|
433 | 433 | :issue => {:project => 'ecookbook'} |
|
434 | 434 | ) |
|
435 | 435 | assert_kind_of Issue, issue |
|
436 | 436 | assert_equal 1, issue.attachments.size |
|
437 | 437 | ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt" |
|
438 | 438 | ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding) |
|
439 | 439 | attachment = issue.attachments.first |
|
440 | 440 | assert_equal ja, attachment.filename |
|
441 | 441 | assert_equal 5, attachment.filesize |
|
442 | 442 | assert File.exist?(attachment.diskfile) |
|
443 | 443 | assert_equal 5, File.size(attachment.diskfile) |
|
444 | 444 | assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest |
|
445 | 445 | end |
|
446 | 446 | |
|
447 | 447 | def test_gmail_with_attachment_ja |
|
448 | 448 | issue = submit_email( |
|
449 | 449 | 'gmail_with_attachment_ja.eml', |
|
450 | 450 | :issue => {:project => 'ecookbook'} |
|
451 | 451 | ) |
|
452 | 452 | assert_kind_of Issue, issue |
|
453 | 453 | assert_equal 1, issue.attachments.size |
|
454 | 454 | ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt" |
|
455 | 455 | ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding) |
|
456 | 456 | attachment = issue.attachments.first |
|
457 | 457 | assert_equal ja, attachment.filename |
|
458 | 458 | assert_equal 5, attachment.filesize |
|
459 | 459 | assert File.exist?(attachment.diskfile) |
|
460 | 460 | assert_equal 5, File.size(attachment.diskfile) |
|
461 | 461 | assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest |
|
462 | 462 | end |
|
463 | 463 | |
|
464 | 464 | def test_thunderbird_with_attachment_latin1 |
|
465 | 465 | issue = submit_email( |
|
466 | 466 | 'thunderbird_with_attachment_iso-8859-1.eml', |
|
467 | 467 | :issue => {:project => 'ecookbook'} |
|
468 | 468 | ) |
|
469 | 469 | assert_kind_of Issue, issue |
|
470 | 470 | assert_equal 1, issue.attachments.size |
|
471 | 471 | u = "" |
|
472 | 472 | u.force_encoding('UTF-8') if u.respond_to?(:force_encoding) |
|
473 | 473 | u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc" |
|
474 | 474 | u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding) |
|
475 | 475 | 11.times { u << u1 } |
|
476 | 476 | attachment = issue.attachments.first |
|
477 | 477 | assert_equal "#{u}.png", attachment.filename |
|
478 | 478 | assert_equal 130, attachment.filesize |
|
479 | 479 | assert File.exist?(attachment.diskfile) |
|
480 | 480 | assert_equal 130, File.size(attachment.diskfile) |
|
481 | 481 | assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest |
|
482 | 482 | end |
|
483 | 483 | |
|
484 | 484 | def test_gmail_with_attachment_latin1 |
|
485 | 485 | issue = submit_email( |
|
486 | 486 | 'gmail_with_attachment_iso-8859-1.eml', |
|
487 | 487 | :issue => {:project => 'ecookbook'} |
|
488 | 488 | ) |
|
489 | 489 | assert_kind_of Issue, issue |
|
490 | 490 | assert_equal 1, issue.attachments.size |
|
491 | 491 | u = "" |
|
492 | 492 | u.force_encoding('UTF-8') if u.respond_to?(:force_encoding) |
|
493 | 493 | u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc" |
|
494 | 494 | u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding) |
|
495 | 495 | 11.times { u << u1 } |
|
496 | 496 | attachment = issue.attachments.first |
|
497 | 497 | assert_equal "#{u}.txt", attachment.filename |
|
498 | 498 | assert_equal 5, attachment.filesize |
|
499 | 499 | assert File.exist?(attachment.diskfile) |
|
500 | 500 | assert_equal 5, File.size(attachment.diskfile) |
|
501 | 501 | assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest |
|
502 | 502 | end |
|
503 | 503 | |
|
504 | 504 | def test_multiple_text_parts |
|
505 | 505 | issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'}) |
|
506 | 506 | assert_include 'first', issue.description |
|
507 | 507 | assert_include 'second', issue.description |
|
508 | 508 | assert_include 'third', issue.description |
|
509 | 509 | end |
|
510 | 510 | |
|
511 | 511 | def test_add_issue_with_iso_8859_1_subject |
|
512 | 512 | issue = submit_email( |
|
513 | 513 | 'subject_as_iso-8859-1.eml', |
|
514 | 514 | :issue => {:project => 'ecookbook'} |
|
515 | 515 | ) |
|
516 | 516 | str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc..." |
|
517 | 517 | str.force_encoding('UTF-8') if str.respond_to?(:force_encoding) |
|
518 | 518 | assert_kind_of Issue, issue |
|
519 | 519 | assert_equal str, issue.subject |
|
520 | 520 | end |
|
521 | 521 | |
|
522 | 522 | def test_add_issue_with_japanese_subject |
|
523 | 523 | issue = submit_email( |
|
524 | 524 | 'subject_japanese_1.eml', |
|
525 | 525 | :issue => {:project => 'ecookbook'} |
|
526 | 526 | ) |
|
527 | 527 | assert_kind_of Issue, issue |
|
528 | 528 | ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88" |
|
529 | 529 | ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding) |
|
530 | 530 | assert_equal ja, issue.subject |
|
531 | 531 | end |
|
532 | 532 | |
|
533 | 533 | def test_add_issue_with_no_subject_header |
|
534 | 534 | issue = submit_email( |
|
535 | 535 | 'no_subject_header.eml', |
|
536 | 536 | :issue => {:project => 'ecookbook'} |
|
537 | 537 | ) |
|
538 | 538 | assert_kind_of Issue, issue |
|
539 | 539 | assert_equal '(no subject)', issue.subject |
|
540 | 540 | end |
|
541 | 541 | |
|
542 | 542 | def test_add_issue_with_mixed_japanese_subject |
|
543 | 543 | issue = submit_email( |
|
544 | 544 | 'subject_japanese_2.eml', |
|
545 | 545 | :issue => {:project => 'ecookbook'} |
|
546 | 546 | ) |
|
547 | 547 | assert_kind_of Issue, issue |
|
548 | 548 | ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88" |
|
549 | 549 | ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding) |
|
550 | 550 | assert_equal ja, issue.subject |
|
551 | 551 | end |
|
552 | 552 | |
|
553 | 553 | def test_should_ignore_emails_from_locked_users |
|
554 | 554 | User.find(2).lock! |
|
555 | 555 | |
|
556 | 556 | MailHandler.any_instance.expects(:dispatch).never |
|
557 | 557 | assert_no_difference 'Issue.count' do |
|
558 | 558 | assert_equal false, submit_email('ticket_on_given_project.eml') |
|
559 | 559 | end |
|
560 | 560 | end |
|
561 | 561 | |
|
562 | 562 | def test_should_ignore_emails_from_emission_address |
|
563 | 563 | Role.anonymous.add_permission!(:add_issues) |
|
564 | 564 | assert_no_difference 'User.count' do |
|
565 | 565 | assert_equal false, |
|
566 | 566 | submit_email( |
|
567 | 567 | 'ticket_from_emission_address.eml', |
|
568 | 568 | :issue => {:project => 'ecookbook'}, |
|
569 | 569 | :unknown_user => 'create' |
|
570 | 570 | ) |
|
571 | 571 | end |
|
572 | 572 | end |
|
573 | 573 | |
|
574 | 574 | def test_should_ignore_auto_replied_emails |
|
575 | 575 | MailHandler.any_instance.expects(:dispatch).never |
|
576 | 576 | [ |
|
577 | 577 | "X-Auto-Response-Suppress: OOF", |
|
578 | 578 | "Auto-Submitted: auto-replied", |
|
579 | 579 | "Auto-Submitted: Auto-Replied", |
|
580 | 580 | "Auto-Submitted: auto-generated" |
|
581 | 581 | ].each do |header| |
|
582 | 582 | raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml')) |
|
583 | 583 | raw = header + "\n" + raw |
|
584 | 584 | |
|
585 | 585 | assert_no_difference 'Issue.count' do |
|
586 | 586 | assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored" |
|
587 | 587 | end |
|
588 | 588 | end |
|
589 | 589 | end |
|
590 | 590 | |
|
591 | 591 | def test_add_issue_should_send_email_notification |
|
592 | 592 | Setting.notified_events = ['issue_added'] |
|
593 | 593 | ActionMailer::Base.deliveries.clear |
|
594 | 594 | # This email contains: 'Project: onlinestore' |
|
595 | 595 | issue = submit_email('ticket_on_given_project.eml') |
|
596 | 596 | assert issue.is_a?(Issue) |
|
597 | 597 | assert_equal 1, ActionMailer::Base.deliveries.size |
|
598 | 598 | end |
|
599 | 599 | |
|
600 | 600 | def test_update_issue |
|
601 | 601 | journal = submit_email('ticket_reply.eml') |
|
602 | 602 | assert journal.is_a?(Journal) |
|
603 | 603 | assert_equal User.find_by_login('jsmith'), journal.user |
|
604 | 604 | assert_equal Issue.find(2), journal.journalized |
|
605 | 605 | assert_match /This is reply/, journal.notes |
|
606 | 606 | assert_equal false, journal.private_notes |
|
607 | 607 | assert_equal 'Feature request', journal.issue.tracker.name |
|
608 | 608 | end |
|
609 | 609 | |
|
610 | 610 | def test_update_issue_with_attribute_changes |
|
611 | 611 | # This email contains: 'Status: Resolved' |
|
612 | 612 | journal = submit_email('ticket_reply_with_status.eml') |
|
613 | 613 | assert journal.is_a?(Journal) |
|
614 | 614 | issue = Issue.find(journal.issue.id) |
|
615 | 615 | assert_equal User.find_by_login('jsmith'), journal.user |
|
616 | 616 | assert_equal Issue.find(2), journal.journalized |
|
617 | 617 | assert_match /This is reply/, journal.notes |
|
618 | 618 | assert_equal 'Feature request', journal.issue.tracker.name |
|
619 | 619 | assert_equal IssueStatus.find_by_name("Resolved"), issue.status |
|
620 | 620 | assert_equal '2010-01-01', issue.start_date.to_s |
|
621 | 621 | assert_equal '2010-12-31', issue.due_date.to_s |
|
622 | 622 | assert_equal User.find_by_login('jsmith'), issue.assigned_to |
|
623 | 623 | assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value |
|
624 | 624 | # keywords should be removed from the email body |
|
625 | 625 | assert !journal.notes.match(/^Status:/i) |
|
626 | 626 | assert !journal.notes.match(/^Start Date:/i) |
|
627 | 627 | end |
|
628 | 628 | |
|
629 | 629 | def test_update_issue_with_attachment |
|
630 | 630 | assert_difference 'Journal.count' do |
|
631 | 631 | assert_difference 'JournalDetail.count' do |
|
632 | 632 | assert_difference 'Attachment.count' do |
|
633 | 633 | assert_no_difference 'Issue.count' do |
|
634 | 634 | journal = submit_email('ticket_with_attachment.eml') do |raw| |
|
635 | 635 | raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories' |
|
636 | 636 | end |
|
637 | 637 | end |
|
638 | 638 | end |
|
639 | 639 | end |
|
640 | 640 | end |
|
641 | 641 | journal = Journal.first(:order => 'id DESC') |
|
642 | 642 | assert_equal Issue.find(2), journal.journalized |
|
643 | 643 | assert_equal 1, journal.details.size |
|
644 | 644 | |
|
645 | 645 | detail = journal.details.first |
|
646 | 646 | assert_equal 'attachment', detail.property |
|
647 | 647 | assert_equal 'Paella.jpg', detail.value |
|
648 | 648 | end |
|
649 | 649 | |
|
650 | 650 | def test_update_issue_should_send_email_notification |
|
651 | 651 | ActionMailer::Base.deliveries.clear |
|
652 | 652 | journal = submit_email('ticket_reply.eml') |
|
653 | 653 | assert journal.is_a?(Journal) |
|
654 | 654 | assert_equal 1, ActionMailer::Base.deliveries.size |
|
655 | 655 | end |
|
656 | 656 | |
|
657 | 657 | def test_update_issue_should_not_set_defaults |
|
658 | 658 | journal = submit_email( |
|
659 | 659 | 'ticket_reply.eml', |
|
660 | 660 | :issue => {:tracker => 'Support request', :priority => 'High'} |
|
661 | 661 | ) |
|
662 | 662 | assert journal.is_a?(Journal) |
|
663 | 663 | assert_match /This is reply/, journal.notes |
|
664 | 664 | assert_equal 'Feature request', journal.issue.tracker.name |
|
665 | 665 | assert_equal 'Normal', journal.issue.priority.name |
|
666 | 666 | end |
|
667 | 667 | |
|
668 | 668 | def test_replying_to_a_private_note_should_add_reply_as_private |
|
669 | 669 | private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2) |
|
670 | 670 | |
|
671 | 671 | assert_difference 'Journal.count' do |
|
672 | 672 | journal = submit_email('ticket_reply.eml') do |email| |
|
673 | 673 | email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>" |
|
674 | 674 | end |
|
675 | 675 | |
|
676 | 676 | assert_kind_of Journal, journal |
|
677 | 677 | assert_match /This is reply/, journal.notes |
|
678 | 678 | assert_equal true, journal.private_notes |
|
679 | 679 | end |
|
680 | 680 | end |
|
681 | 681 | |
|
682 | 682 | def test_reply_to_a_message |
|
683 | 683 | m = submit_email('message_reply.eml') |
|
684 | 684 | assert m.is_a?(Message) |
|
685 | 685 | assert !m.new_record? |
|
686 | 686 | m.reload |
|
687 | 687 | assert_equal 'Reply via email', m.subject |
|
688 | 688 | # The email replies to message #2 which is part of the thread of message #1 |
|
689 | 689 | assert_equal Message.find(1), m.parent |
|
690 | 690 | end |
|
691 | 691 | |
|
692 | 692 | def test_reply_to_a_message_by_subject |
|
693 | 693 | m = submit_email('message_reply_by_subject.eml') |
|
694 | 694 | assert m.is_a?(Message) |
|
695 | 695 | assert !m.new_record? |
|
696 | 696 | m.reload |
|
697 | 697 | assert_equal 'Reply to the first post', m.subject |
|
698 | 698 | assert_equal Message.find(1), m.parent |
|
699 | 699 | end |
|
700 | 700 | |
|
701 | 701 | def test_should_strip_tags_of_html_only_emails |
|
702 | 702 | issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'}) |
|
703 | 703 | assert issue.is_a?(Issue) |
|
704 | 704 | assert !issue.new_record? |
|
705 | 705 | issue.reload |
|
706 | 706 | assert_equal 'HTML email', issue.subject |
|
707 | 707 | assert_equal 'This is a html-only email.', issue.description |
|
708 | 708 | end |
|
709 | 709 | |
|
710 | 710 | test "truncate emails with no setting should add the entire email into the issue" do |
|
711 | 711 | with_settings :mail_handler_body_delimiters => '' do |
|
712 | 712 | issue = submit_email('ticket_on_given_project.eml') |
|
713 | 713 | assert_issue_created(issue) |
|
714 | 714 | assert issue.description.include?('---') |
|
715 | 715 | assert issue.description.include?('This paragraph is after the delimiter') |
|
716 | 716 | end |
|
717 | 717 | end |
|
718 | 718 | |
|
719 | 719 | test "truncate emails with a single string should truncate the email at the delimiter for the issue" do |
|
720 | 720 | with_settings :mail_handler_body_delimiters => '---' do |
|
721 | 721 | issue = submit_email('ticket_on_given_project.eml') |
|
722 | 722 | assert_issue_created(issue) |
|
723 | 723 | assert issue.description.include?('This paragraph is before delimiters') |
|
724 | 724 | assert issue.description.include?('--- This line starts with a delimiter') |
|
725 | 725 | assert !issue.description.match(/^---$/) |
|
726 | 726 | assert !issue.description.include?('This paragraph is after the delimiter') |
|
727 | 727 | end |
|
728 | 728 | end |
|
729 | 729 | |
|
730 | 730 | test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do |
|
731 | 731 | with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do |
|
732 | 732 | journal = submit_email('issue_update_with_quoted_reply_above.eml') |
|
733 | 733 | assert journal.is_a?(Journal) |
|
734 | 734 | assert journal.notes.include?('An update to the issue by the sender.') |
|
735 | 735 | assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---")) |
|
736 | 736 | assert !journal.notes.include?('Looks like the JSON api for projects was missed.') |
|
737 | 737 | end |
|
738 | 738 | end |
|
739 | 739 | |
|
740 | 740 | test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do |
|
741 | 741 | with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do |
|
742 | 742 | journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml') |
|
743 | 743 | assert journal.is_a?(Journal) |
|
744 | 744 | assert journal.notes.include?('An update to the issue by the sender.') |
|
745 | 745 | assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---")) |
|
746 | 746 | assert !journal.notes.include?('Looks like the JSON api for projects was missed.') |
|
747 | 747 | end |
|
748 | 748 | end |
|
749 | 749 | |
|
750 | 750 | test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do |
|
751 | 751 | with_settings :mail_handler_body_delimiters => "---\nBREAK" do |
|
752 | 752 | issue = submit_email('ticket_on_given_project.eml') |
|
753 | 753 | assert_issue_created(issue) |
|
754 | 754 | assert issue.description.include?('This paragraph is before delimiters') |
|
755 | 755 | assert !issue.description.include?('BREAK') |
|
756 | 756 | assert !issue.description.include?('This paragraph is between delimiters') |
|
757 | 757 | assert !issue.description.match(/^---$/) |
|
758 | 758 | assert !issue.description.include?('This paragraph is after the delimiter') |
|
759 | 759 | end |
|
760 | 760 | end |
|
761 | 761 | |
|
762 | def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored | |
|
763 | with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do | |
|
764 | issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'}) | |
|
765 | assert issue.is_a?(Issue) | |
|
766 | assert !issue.new_record? | |
|
767 | assert_equal 0, issue.reload.attachments.size | |
|
768 | end | |
|
769 | end | |
|
770 | ||
|
771 | def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached | |
|
772 | with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do | |
|
773 | issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'}) | |
|
774 | assert issue.is_a?(Issue) | |
|
775 | assert !issue.new_record? | |
|
776 | assert_equal 1, issue.reload.attachments.size | |
|
777 | end | |
|
778 | end | |
|
779 | ||
|
762 | 780 | def test_email_with_long_subject_line |
|
763 | 781 | issue = submit_email('ticket_with_long_subject.eml') |
|
764 | 782 | assert issue.is_a?(Issue) |
|
765 | 783 | assert_equal issue.subject, 'New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...'[0,255] |
|
766 | 784 | end |
|
767 | 785 | |
|
768 | 786 | def test_new_user_from_attributes_should_return_valid_user |
|
769 | 787 | to_test = { |
|
770 | 788 | # [address, name] => [login, firstname, lastname] |
|
771 | 789 | ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'], |
|
772 | 790 | ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'], |
|
773 | 791 | ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'], |
|
774 | 792 | ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'], |
|
775 | 793 | ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'], |
|
776 | 794 | ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh'] |
|
777 | 795 | } |
|
778 | 796 | |
|
779 | 797 | to_test.each do |attrs, expected| |
|
780 | 798 | user = MailHandler.new_user_from_attributes(attrs.first, attrs.last) |
|
781 | 799 | |
|
782 | 800 | assert user.valid?, user.errors.full_messages.to_s |
|
783 | 801 | assert_equal attrs.first, user.mail |
|
784 | 802 | assert_equal expected[0], user.login |
|
785 | 803 | assert_equal expected[1], user.firstname |
|
786 | 804 | assert_equal expected[2], user.lastname |
|
787 | 805 | assert_equal 'only_my_events', user.mail_notification |
|
788 | 806 | end |
|
789 | 807 | end |
|
790 | 808 | |
|
791 | 809 | def test_new_user_from_attributes_should_use_default_login_if_invalid |
|
792 | 810 | user = MailHandler.new_user_from_attributes('foo+bar@example.net') |
|
793 | 811 | assert user.valid? |
|
794 | 812 | assert user.login =~ /^user[a-f0-9]+$/ |
|
795 | 813 | assert_equal 'foo+bar@example.net', user.mail |
|
796 | 814 | end |
|
797 | 815 | |
|
798 | 816 | def test_new_user_with_utf8_encoded_fullname_should_be_decoded |
|
799 | 817 | assert_difference 'User.count' do |
|
800 | 818 | issue = submit_email( |
|
801 | 819 | 'fullname_of_sender_as_utf8_encoded.eml', |
|
802 | 820 | :issue => {:project => 'ecookbook'}, |
|
803 | 821 | :unknown_user => 'create' |
|
804 | 822 | ) |
|
805 | 823 | end |
|
806 | 824 | |
|
807 | 825 | user = User.first(:order => 'id DESC') |
|
808 | 826 | assert_equal "foo@example.org", user.mail |
|
809 | 827 | str1 = "\xc3\x84\xc3\xa4" |
|
810 | 828 | str2 = "\xc3\x96\xc3\xb6" |
|
811 | 829 | str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding) |
|
812 | 830 | str2.force_encoding('UTF-8') if str2.respond_to?(:force_encoding) |
|
813 | 831 | assert_equal str1, user.firstname |
|
814 | 832 | assert_equal str2, user.lastname |
|
815 | 833 | end |
|
816 | 834 | |
|
817 | 835 | def test_extract_options_from_env_should_return_options |
|
818 | 836 | options = MailHandler.extract_options_from_env({ |
|
819 | 837 | 'tracker' => 'defect', |
|
820 | 838 | 'project' => 'foo', |
|
821 | 839 | 'unknown_user' => 'create' |
|
822 | 840 | }) |
|
823 | 841 | |
|
824 | 842 | assert_equal({ |
|
825 | 843 | :issue => {:tracker => 'defect', :project => 'foo'}, |
|
826 | 844 | :unknown_user => 'create' |
|
827 | 845 | }, options) |
|
828 | 846 | end |
|
829 | 847 | |
|
830 | 848 | private |
|
831 | 849 | |
|
832 | 850 | def submit_email(filename, options={}) |
|
833 | 851 | raw = IO.read(File.join(FIXTURES_PATH, filename)) |
|
834 | 852 | yield raw if block_given? |
|
835 | 853 | MailHandler.receive(raw, options) |
|
836 | 854 | end |
|
837 | 855 | |
|
838 | 856 | def assert_issue_created(issue) |
|
839 | 857 | assert issue.is_a?(Issue) |
|
840 | 858 | assert !issue.new_record? |
|
841 | 859 | issue.reload |
|
842 | 860 | end |
|
843 | 861 | end |
General Comments 0
You need to be logged in to leave comments.
Login now