@@ -1,1374 +1,1374 | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | # |
|
3 | 3 | # Redmine - project management software |
|
4 | 4 | # Copyright (C) 2006-2016 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 'forwardable' |
|
21 | 21 | require 'cgi' |
|
22 | 22 | |
|
23 | 23 | module ApplicationHelper |
|
24 | 24 | include Redmine::WikiFormatting::Macros::Definitions |
|
25 | 25 | include Redmine::I18n |
|
26 | 26 | include GravatarHelper::PublicMethods |
|
27 | 27 | include Redmine::Pagination::Helper |
|
28 | 28 | include Redmine::SudoMode::Helper |
|
29 | 29 | include Redmine::Themes::Helper |
|
30 | 30 | include Redmine::Hook::Helper |
|
31 | 31 | include Redmine::Helpers::URL |
|
32 | 32 | |
|
33 | 33 | extend Forwardable |
|
34 | 34 | def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter |
|
35 | 35 | |
|
36 | 36 | # Return true if user is authorized for controller/action, otherwise false |
|
37 | 37 | def authorize_for(controller, action) |
|
38 | 38 | User.current.allowed_to?({:controller => controller, :action => action}, @project) |
|
39 | 39 | end |
|
40 | 40 | |
|
41 | 41 | # Display a link if user is authorized |
|
42 | 42 | # |
|
43 | 43 | # @param [String] name Anchor text (passed to link_to) |
|
44 | 44 | # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized |
|
45 | 45 | # @param [optional, Hash] html_options Options passed to link_to |
|
46 | 46 | # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to |
|
47 | 47 | def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference) |
|
48 | 48 | link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action]) |
|
49 | 49 | end |
|
50 | 50 | |
|
51 | 51 | # Displays a link to user's account page if active |
|
52 | 52 | def link_to_user(user, options={}) |
|
53 | 53 | if user.is_a?(User) |
|
54 | 54 | name = h(user.name(options[:format])) |
|
55 | 55 | if user.active? || (User.current.admin? && user.logged?) |
|
56 | 56 | link_to name, user_path(user), :class => user.css_classes |
|
57 | 57 | else |
|
58 | 58 | name |
|
59 | 59 | end |
|
60 | 60 | else |
|
61 | 61 | h(user.to_s) |
|
62 | 62 | end |
|
63 | 63 | end |
|
64 | 64 | |
|
65 | 65 | # Displays a link to +issue+ with its subject. |
|
66 | 66 | # Examples: |
|
67 | 67 | # |
|
68 | 68 | # link_to_issue(issue) # => Defect #6: This is the subject |
|
69 | 69 | # link_to_issue(issue, :truncate => 6) # => Defect #6: This i... |
|
70 | 70 | # link_to_issue(issue, :subject => false) # => Defect #6 |
|
71 | 71 | # link_to_issue(issue, :project => true) # => Foo - Defect #6 |
|
72 | 72 | # link_to_issue(issue, :subject => false, :tracker => false) # => #6 |
|
73 | 73 | # |
|
74 | 74 | def link_to_issue(issue, options={}) |
|
75 | 75 | title = nil |
|
76 | 76 | subject = nil |
|
77 | 77 | text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}" |
|
78 | 78 | if options[:subject] == false |
|
79 | 79 | title = issue.subject.truncate(60) |
|
80 | 80 | else |
|
81 | 81 | subject = issue.subject |
|
82 | 82 | if truncate_length = options[:truncate] |
|
83 | 83 | subject = subject.truncate(truncate_length) |
|
84 | 84 | end |
|
85 | 85 | end |
|
86 | 86 | only_path = options[:only_path].nil? ? true : options[:only_path] |
|
87 | 87 | s = link_to(text, issue_url(issue, :only_path => only_path), |
|
88 | 88 | :class => issue.css_classes, :title => title) |
|
89 | 89 | s << h(": #{subject}") if subject |
|
90 | 90 | s = h("#{issue.project} - ") + s if options[:project] |
|
91 | 91 | s |
|
92 | 92 | end |
|
93 | 93 | |
|
94 | 94 | # Generates a link to an attachment. |
|
95 | 95 | # Options: |
|
96 | 96 | # * :text - Link text (default to attachment filename) |
|
97 | 97 | # * :download - Force download (default: false) |
|
98 | 98 | def link_to_attachment(attachment, options={}) |
|
99 | 99 | text = options.delete(:text) || attachment.filename |
|
100 | 100 | route_method = options.delete(:download) ? :download_named_attachment_url : :named_attachment_url |
|
101 | 101 | html_options = options.slice!(:only_path) |
|
102 | 102 | options[:only_path] = true unless options.key?(:only_path) |
|
103 | 103 | url = send(route_method, attachment, attachment.filename, options) |
|
104 | 104 | link_to text, url, html_options |
|
105 | 105 | end |
|
106 | 106 | |
|
107 | 107 | # Generates a link to a SCM revision |
|
108 | 108 | # Options: |
|
109 | 109 | # * :text - Link text (default to the formatted revision) |
|
110 | 110 | def link_to_revision(revision, repository, options={}) |
|
111 | 111 | if repository.is_a?(Project) |
|
112 | 112 | repository = repository.repository |
|
113 | 113 | end |
|
114 | 114 | text = options.delete(:text) || format_revision(revision) |
|
115 | 115 | rev = revision.respond_to?(:identifier) ? revision.identifier : revision |
|
116 | 116 | link_to( |
|
117 | 117 | h(text), |
|
118 | 118 | {:controller => 'repositories', :action => 'revision', :id => repository.project, :repository_id => repository.identifier_param, :rev => rev}, |
|
119 | 119 | :title => l(:label_revision_id, format_revision(revision)), |
|
120 | 120 | :accesskey => options[:accesskey] |
|
121 | 121 | ) |
|
122 | 122 | end |
|
123 | 123 | |
|
124 | 124 | # Generates a link to a message |
|
125 | 125 | def link_to_message(message, options={}, html_options = nil) |
|
126 | 126 | link_to( |
|
127 | 127 | message.subject.truncate(60), |
|
128 | 128 | board_message_url(message.board_id, message.parent_id || message.id, { |
|
129 | 129 | :r => (message.parent_id && message.id), |
|
130 | 130 | :anchor => (message.parent_id ? "message-#{message.id}" : nil), |
|
131 | 131 | :only_path => true |
|
132 | 132 | }.merge(options)), |
|
133 | 133 | html_options |
|
134 | 134 | ) |
|
135 | 135 | end |
|
136 | 136 | |
|
137 | 137 | # Generates a link to a project if active |
|
138 | 138 | # Examples: |
|
139 | 139 | # |
|
140 | 140 | # link_to_project(project) # => link to the specified project overview |
|
141 | 141 | # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options |
|
142 | 142 | # link_to_project(project, {}, :class => "project") # => html options with default url (project overview) |
|
143 | 143 | # |
|
144 | 144 | def link_to_project(project, options={}, html_options = nil) |
|
145 | 145 | if project.archived? |
|
146 | 146 | h(project.name) |
|
147 | 147 | else |
|
148 | 148 | link_to project.name, |
|
149 | 149 | project_url(project, {:only_path => true}.merge(options)), |
|
150 | 150 | html_options |
|
151 | 151 | end |
|
152 | 152 | end |
|
153 | 153 | |
|
154 | 154 | # Generates a link to a project settings if active |
|
155 | 155 | def link_to_project_settings(project, options={}, html_options=nil) |
|
156 | 156 | if project.active? |
|
157 | 157 | link_to project.name, settings_project_path(project, options), html_options |
|
158 | 158 | elsif project.archived? |
|
159 | 159 | h(project.name) |
|
160 | 160 | else |
|
161 | 161 | link_to project.name, project_path(project, options), html_options |
|
162 | 162 | end |
|
163 | 163 | end |
|
164 | 164 | |
|
165 | 165 | # Generates a link to a version |
|
166 | 166 | def link_to_version(version, options = {}) |
|
167 | 167 | return '' unless version && version.is_a?(Version) |
|
168 | 168 | options = {:title => format_date(version.effective_date)}.merge(options) |
|
169 | 169 | link_to_if version.visible?, format_version_name(version), version_path(version), options |
|
170 | 170 | end |
|
171 | 171 | |
|
172 | 172 | # Helper that formats object for html or text rendering |
|
173 | 173 | def format_object(object, html=true, &block) |
|
174 | 174 | if block_given? |
|
175 | 175 | object = yield object |
|
176 | 176 | end |
|
177 | 177 | case object.class.name |
|
178 | 178 | when 'Array' |
|
179 | 179 | object.map {|o| format_object(o, html)}.join(', ').html_safe |
|
180 | 180 | when 'Time' |
|
181 | 181 | format_time(object) |
|
182 | 182 | when 'Date' |
|
183 | 183 | format_date(object) |
|
184 | 184 | when 'Fixnum' |
|
185 | 185 | object.to_s |
|
186 | 186 | when 'Float' |
|
187 | 187 | sprintf "%.2f", object |
|
188 | 188 | when 'User' |
|
189 | 189 | html ? link_to_user(object) : object.to_s |
|
190 | 190 | when 'Project' |
|
191 | 191 | html ? link_to_project(object) : object.to_s |
|
192 | 192 | when 'Version' |
|
193 | 193 | html ? link_to_version(object) : object.to_s |
|
194 | 194 | when 'TrueClass' |
|
195 | 195 | l(:general_text_Yes) |
|
196 | 196 | when 'FalseClass' |
|
197 | 197 | l(:general_text_No) |
|
198 | 198 | when 'Issue' |
|
199 | 199 | object.visible? && html ? link_to_issue(object) : "##{object.id}" |
|
200 | 200 | when 'Attachment' |
|
201 | 201 | html ? link_to_attachment(object, :download => true) : object.filename |
|
202 | 202 | when 'CustomValue', 'CustomFieldValue' |
|
203 | 203 | if object.custom_field |
|
204 | 204 | f = object.custom_field.format.formatted_custom_value(self, object, html) |
|
205 | 205 | if f.nil? || f.is_a?(String) |
|
206 | 206 | f |
|
207 | 207 | else |
|
208 | 208 | format_object(f, html, &block) |
|
209 | 209 | end |
|
210 | 210 | else |
|
211 | 211 | object.value.to_s |
|
212 | 212 | end |
|
213 | 213 | else |
|
214 | 214 | html ? h(object) : object.to_s |
|
215 | 215 | end |
|
216 | 216 | end |
|
217 | 217 | |
|
218 | 218 | def wiki_page_path(page, options={}) |
|
219 | 219 | url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options)) |
|
220 | 220 | end |
|
221 | 221 | |
|
222 | 222 | def thumbnail_tag(attachment) |
|
223 | 223 | link_to image_tag(thumbnail_path(attachment)), |
|
224 | 224 | named_attachment_path(attachment, attachment.filename), |
|
225 | 225 | :title => attachment.filename |
|
226 | 226 | end |
|
227 | 227 | |
|
228 | 228 | def toggle_link(name, id, options={}) |
|
229 | 229 | onclick = "$('##{id}').toggle(); " |
|
230 | 230 | onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ") |
|
231 | 231 | onclick << "return false;" |
|
232 | 232 | link_to(name, "#", :onclick => onclick) |
|
233 | 233 | end |
|
234 | 234 | |
|
235 | 235 | # Used to format item titles on the activity view |
|
236 | 236 | def format_activity_title(text) |
|
237 | 237 | text |
|
238 | 238 | end |
|
239 | 239 | |
|
240 | 240 | def format_activity_day(date) |
|
241 | 241 | date == User.current.today ? l(:label_today).titleize : format_date(date) |
|
242 | 242 | end |
|
243 | 243 | |
|
244 | 244 | def format_activity_description(text) |
|
245 | 245 | h(text.to_s.truncate(120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...') |
|
246 | 246 | ).gsub(/[\r\n]+/, "<br />").html_safe |
|
247 | 247 | end |
|
248 | 248 | |
|
249 | 249 | def format_version_name(version) |
|
250 | 250 | if version.project == @project |
|
251 | 251 | h(version) |
|
252 | 252 | else |
|
253 | 253 | h("#{version.project} - #{version}") |
|
254 | 254 | end |
|
255 | 255 | end |
|
256 | 256 | |
|
257 | 257 | def due_date_distance_in_words(date) |
|
258 | 258 | if date |
|
259 | 259 | l((date < User.current.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(User.current.today, date)) |
|
260 | 260 | end |
|
261 | 261 | end |
|
262 | 262 | |
|
263 | 263 | # Renders a tree of projects as a nested set of unordered lists |
|
264 | 264 | # The given collection may be a subset of the whole project tree |
|
265 | 265 | # (eg. some intermediate nodes are private and can not be seen) |
|
266 | 266 | def render_project_nested_lists(projects, &block) |
|
267 | 267 | s = '' |
|
268 | 268 | if projects.any? |
|
269 | 269 | ancestors = [] |
|
270 | 270 | original_project = @project |
|
271 | 271 | projects.sort_by(&:lft).each do |project| |
|
272 | 272 | # set the project environment to please macros. |
|
273 | 273 | @project = project |
|
274 | 274 | if (ancestors.empty? || project.is_descendant_of?(ancestors.last)) |
|
275 | 275 | s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n" |
|
276 | 276 | else |
|
277 | 277 | ancestors.pop |
|
278 | 278 | s << "</li>" |
|
279 | 279 | while (ancestors.any? && !project.is_descendant_of?(ancestors.last)) |
|
280 | 280 | ancestors.pop |
|
281 | 281 | s << "</ul></li>\n" |
|
282 | 282 | end |
|
283 | 283 | end |
|
284 | 284 | classes = (ancestors.empty? ? 'root' : 'child') |
|
285 | 285 | s << "<li class='#{classes}'><div class='#{classes}'>" |
|
286 | 286 | s << h(block_given? ? capture(project, &block) : project.name) |
|
287 | 287 | s << "</div>\n" |
|
288 | 288 | ancestors << project |
|
289 | 289 | end |
|
290 | 290 | s << ("</li></ul>\n" * ancestors.size) |
|
291 | 291 | @project = original_project |
|
292 | 292 | end |
|
293 | 293 | s.html_safe |
|
294 | 294 | end |
|
295 | 295 | |
|
296 | 296 | def render_page_hierarchy(pages, node=nil, options={}) |
|
297 | 297 | content = '' |
|
298 | 298 | if pages[node] |
|
299 | 299 | content << "<ul class=\"pages-hierarchy\">\n" |
|
300 | 300 | pages[node].each do |page| |
|
301 | 301 | content << "<li>" |
|
302 | 302 | content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title, :version => nil}, |
|
303 | 303 | :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil)) |
|
304 | 304 | content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id] |
|
305 | 305 | content << "</li>\n" |
|
306 | 306 | end |
|
307 | 307 | content << "</ul>\n" |
|
308 | 308 | end |
|
309 | 309 | content.html_safe |
|
310 | 310 | end |
|
311 | 311 | |
|
312 | 312 | # Renders flash messages |
|
313 | 313 | def render_flash_messages |
|
314 | 314 | s = '' |
|
315 | 315 | flash.each do |k,v| |
|
316 | 316 | s << content_tag('div', v.html_safe, :class => "flash #{k}", :id => "flash_#{k}") |
|
317 | 317 | end |
|
318 | 318 | s.html_safe |
|
319 | 319 | end |
|
320 | 320 | |
|
321 | 321 | # Renders tabs and their content |
|
322 | 322 | def render_tabs(tabs, selected=params[:tab]) |
|
323 | 323 | if tabs.any? |
|
324 | 324 | unless tabs.detect {|tab| tab[:name] == selected} |
|
325 | 325 | selected = nil |
|
326 | 326 | end |
|
327 | 327 | selected ||= tabs.first[:name] |
|
328 | 328 | render :partial => 'common/tabs', :locals => {:tabs => tabs, :selected_tab => selected} |
|
329 | 329 | else |
|
330 | 330 | content_tag 'p', l(:label_no_data), :class => "nodata" |
|
331 | 331 | end |
|
332 | 332 | end |
|
333 | 333 | |
|
334 | 334 | # Renders the project quick-jump box |
|
335 | 335 | def render_project_jump_box |
|
336 | 336 | return unless User.current.logged? |
|
337 | 337 | projects = User.current.projects.active.select(:id, :name, :identifier, :lft, :rgt).to_a |
|
338 | 338 | if projects.any? |
|
339 | 339 | options = |
|
340 | 340 | ("<option value=''>#{ l(:label_jump_to_a_project) }</option>" + |
|
341 | 341 | '<option value="" disabled="disabled">---</option>').html_safe |
|
342 | 342 | |
|
343 | 343 | options << project_tree_options_for_select(projects, :selected => @project) do |p| |
|
344 | 344 | { :value => project_path(:id => p, :jump => current_menu_item) } |
|
345 | 345 | end |
|
346 | 346 | |
|
347 | 347 | content_tag( :span, nil, :class => 'jump-box-arrow') + |
|
348 | 348 | select_tag('project_quick_jump_box', options, :onchange => 'if (this.value != \'\') { window.location = this.value; }') |
|
349 | 349 | end |
|
350 | 350 | end |
|
351 | 351 | |
|
352 | 352 | def project_tree_options_for_select(projects, options = {}) |
|
353 | 353 | s = ''.html_safe |
|
354 | 354 | if blank_text = options[:include_blank] |
|
355 | 355 | if blank_text == true |
|
356 | 356 | blank_text = ' '.html_safe |
|
357 | 357 | end |
|
358 | 358 | s << content_tag('option', blank_text, :value => '') |
|
359 | 359 | end |
|
360 | 360 | project_tree(projects) do |project, level| |
|
361 | 361 | name_prefix = (level > 0 ? ' ' * 2 * level + '» ' : '').html_safe |
|
362 | 362 | tag_options = {:value => project.id} |
|
363 | 363 | if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project)) |
|
364 | 364 | tag_options[:selected] = 'selected' |
|
365 | 365 | else |
|
366 | 366 | tag_options[:selected] = nil |
|
367 | 367 | end |
|
368 | 368 | tag_options.merge!(yield(project)) if block_given? |
|
369 | 369 | s << content_tag('option', name_prefix + h(project), tag_options) |
|
370 | 370 | end |
|
371 | 371 | s.html_safe |
|
372 | 372 | end |
|
373 | 373 | |
|
374 | 374 | # Yields the given block for each project with its level in the tree |
|
375 | 375 | # |
|
376 | 376 | # Wrapper for Project#project_tree |
|
377 | 377 | def project_tree(projects, options={}, &block) |
|
378 | 378 | Project.project_tree(projects, options, &block) |
|
379 | 379 | end |
|
380 | 380 | |
|
381 | 381 | def principals_check_box_tags(name, principals) |
|
382 | 382 | s = '' |
|
383 | 383 | principals.each do |principal| |
|
384 | 384 | s << "<label>#{ check_box_tag name, principal.id, false, :id => nil } #{h principal}</label>\n" |
|
385 | 385 | end |
|
386 | 386 | s.html_safe |
|
387 | 387 | end |
|
388 | 388 | |
|
389 | 389 | # Returns a string for users/groups option tags |
|
390 | 390 | def principals_options_for_select(collection, selected=nil) |
|
391 | 391 | s = '' |
|
392 | 392 | if collection.include?(User.current) |
|
393 | 393 | s << content_tag('option', "<< #{l(:label_me)} >>", :value => User.current.id) |
|
394 | 394 | end |
|
395 | 395 | groups = '' |
|
396 | 396 | collection.sort.each do |element| |
|
397 | 397 | selected_attribute = ' selected="selected"' if option_value_selected?(element, selected) || element.id.to_s == selected |
|
398 | 398 | (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>) |
|
399 | 399 | end |
|
400 | 400 | unless groups.empty? |
|
401 | 401 | s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>) |
|
402 | 402 | end |
|
403 | 403 | s.html_safe |
|
404 | 404 | end |
|
405 | 405 | |
|
406 | 406 | def option_tag(name, text, value, selected=nil, options={}) |
|
407 | 407 | content_tag 'option', value, options.merge(:value => value, :selected => (value == selected)) |
|
408 | 408 | end |
|
409 | 409 | |
|
410 | 410 | def truncate_single_line_raw(string, length) |
|
411 | 411 | string.to_s.truncate(length).gsub(%r{[\r\n]+}m, ' ') |
|
412 | 412 | end |
|
413 | 413 | |
|
414 | 414 | # Truncates at line break after 250 characters or options[:length] |
|
415 | 415 | def truncate_lines(string, options={}) |
|
416 | 416 | length = options[:length] || 250 |
|
417 | 417 | if string.to_s =~ /\A(.{#{length}}.*?)$/m |
|
418 | 418 | "#{$1}..." |
|
419 | 419 | else |
|
420 | 420 | string |
|
421 | 421 | end |
|
422 | 422 | end |
|
423 | 423 | |
|
424 | 424 | def anchor(text) |
|
425 | 425 | text.to_s.gsub(' ', '_') |
|
426 | 426 | end |
|
427 | 427 | |
|
428 | 428 | def html_hours(text) |
|
429 |
text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec"> |
|
|
429 | text.gsub(%r{(\d+)([\.:])(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">\2\3</span>').html_safe | |
|
430 | 430 | end |
|
431 | 431 | |
|
432 | 432 | def authoring(created, author, options={}) |
|
433 | 433 | l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe |
|
434 | 434 | end |
|
435 | 435 | |
|
436 | 436 | def time_tag(time) |
|
437 | 437 | text = distance_of_time_in_words(Time.now, time) |
|
438 | 438 | if @project |
|
439 | 439 | link_to(text, project_activity_path(@project, :from => User.current.time_to_date(time)), :title => format_time(time)) |
|
440 | 440 | else |
|
441 | 441 | content_tag('abbr', text, :title => format_time(time)) |
|
442 | 442 | end |
|
443 | 443 | end |
|
444 | 444 | |
|
445 | 445 | def syntax_highlight_lines(name, content) |
|
446 | 446 | lines = [] |
|
447 | 447 | syntax_highlight(name, content).each_line { |line| lines << line } |
|
448 | 448 | lines |
|
449 | 449 | end |
|
450 | 450 | |
|
451 | 451 | def syntax_highlight(name, content) |
|
452 | 452 | Redmine::SyntaxHighlighting.highlight_by_filename(content, name) |
|
453 | 453 | end |
|
454 | 454 | |
|
455 | 455 | def to_path_param(path) |
|
456 | 456 | str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/") |
|
457 | 457 | str.blank? ? nil : str |
|
458 | 458 | end |
|
459 | 459 | |
|
460 | 460 | def reorder_links(name, url, method = :post) |
|
461 | 461 | # TODO: remove associated styles from application.css too |
|
462 | 462 | ActiveSupport::Deprecation.warn "Application#reorder_links will be removed in Redmine 4." |
|
463 | 463 | |
|
464 | 464 | link_to(l(:label_sort_highest), |
|
465 | 465 | url.merge({"#{name}[move_to]" => 'highest'}), :method => method, |
|
466 | 466 | :title => l(:label_sort_highest), :class => 'icon-only icon-move-top') + |
|
467 | 467 | link_to(l(:label_sort_higher), |
|
468 | 468 | url.merge({"#{name}[move_to]" => 'higher'}), :method => method, |
|
469 | 469 | :title => l(:label_sort_higher), :class => 'icon-only icon-move-up') + |
|
470 | 470 | link_to(l(:label_sort_lower), |
|
471 | 471 | url.merge({"#{name}[move_to]" => 'lower'}), :method => method, |
|
472 | 472 | :title => l(:label_sort_lower), :class => 'icon-only icon-move-down') + |
|
473 | 473 | link_to(l(:label_sort_lowest), |
|
474 | 474 | url.merge({"#{name}[move_to]" => 'lowest'}), :method => method, |
|
475 | 475 | :title => l(:label_sort_lowest), :class => 'icon-only icon-move-bottom') |
|
476 | 476 | end |
|
477 | 477 | |
|
478 | 478 | def reorder_handle(object, options={}) |
|
479 | 479 | data = { |
|
480 | 480 | :reorder_url => options[:url] || url_for(object), |
|
481 | 481 | :reorder_param => options[:param] || object.class.name.underscore |
|
482 | 482 | } |
|
483 | 483 | content_tag('span', '', |
|
484 | 484 | :class => "sort-handle", |
|
485 | 485 | :data => data, |
|
486 | 486 | :title => l(:button_sort)) |
|
487 | 487 | end |
|
488 | 488 | |
|
489 | 489 | def breadcrumb(*args) |
|
490 | 490 | elements = args.flatten |
|
491 | 491 | elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil |
|
492 | 492 | end |
|
493 | 493 | |
|
494 | 494 | def other_formats_links(&block) |
|
495 | 495 | concat('<p class="other-formats">'.html_safe + l(:label_export_to)) |
|
496 | 496 | yield Redmine::Views::OtherFormatsBuilder.new(self) |
|
497 | 497 | concat('</p>'.html_safe) |
|
498 | 498 | end |
|
499 | 499 | |
|
500 | 500 | def page_header_title |
|
501 | 501 | if @project.nil? || @project.new_record? |
|
502 | 502 | h(Setting.app_title) |
|
503 | 503 | else |
|
504 | 504 | b = [] |
|
505 | 505 | ancestors = (@project.root? ? [] : @project.ancestors.visible.to_a) |
|
506 | 506 | if ancestors.any? |
|
507 | 507 | root = ancestors.shift |
|
508 | 508 | b << link_to_project(root, {:jump => current_menu_item}, :class => 'root') |
|
509 | 509 | if ancestors.size > 2 |
|
510 | 510 | b << "\xe2\x80\xa6" |
|
511 | 511 | ancestors = ancestors[-2, 2] |
|
512 | 512 | end |
|
513 | 513 | b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') } |
|
514 | 514 | end |
|
515 | 515 | b << content_tag(:span, h(@project), class: 'current-project') |
|
516 | 516 | if b.size > 1 |
|
517 | 517 | separator = content_tag(:span, ' » '.html_safe, class: 'separator') |
|
518 | 518 | path = safe_join(b[0..-2], separator) + separator |
|
519 | 519 | b = [content_tag(:span, path.html_safe, class: 'breadcrumbs'), b[-1]] |
|
520 | 520 | end |
|
521 | 521 | safe_join b |
|
522 | 522 | end |
|
523 | 523 | end |
|
524 | 524 | |
|
525 | 525 | # Returns a h2 tag and sets the html title with the given arguments |
|
526 | 526 | def title(*args) |
|
527 | 527 | strings = args.map do |arg| |
|
528 | 528 | if arg.is_a?(Array) && arg.size >= 2 |
|
529 | 529 | link_to(*arg) |
|
530 | 530 | else |
|
531 | 531 | h(arg.to_s) |
|
532 | 532 | end |
|
533 | 533 | end |
|
534 | 534 | html_title args.reverse.map {|s| (s.is_a?(Array) ? s.first : s).to_s} |
|
535 | 535 | content_tag('h2', strings.join(' » ').html_safe) |
|
536 | 536 | end |
|
537 | 537 | |
|
538 | 538 | # Sets the html title |
|
539 | 539 | # Returns the html title when called without arguments |
|
540 | 540 | # Current project name and app_title and automatically appended |
|
541 | 541 | # Exemples: |
|
542 | 542 | # html_title 'Foo', 'Bar' |
|
543 | 543 | # html_title # => 'Foo - Bar - My Project - Redmine' |
|
544 | 544 | def html_title(*args) |
|
545 | 545 | if args.empty? |
|
546 | 546 | title = @html_title || [] |
|
547 | 547 | title << @project.name if @project |
|
548 | 548 | title << Setting.app_title unless Setting.app_title == title.last |
|
549 | 549 | title.reject(&:blank?).join(' - ') |
|
550 | 550 | else |
|
551 | 551 | @html_title ||= [] |
|
552 | 552 | @html_title += args |
|
553 | 553 | end |
|
554 | 554 | end |
|
555 | 555 | |
|
556 | 556 | # Returns the theme, controller name, and action as css classes for the |
|
557 | 557 | # HTML body. |
|
558 | 558 | def body_css_classes |
|
559 | 559 | css = [] |
|
560 | 560 | if theme = Redmine::Themes.theme(Setting.ui_theme) |
|
561 | 561 | css << 'theme-' + theme.name |
|
562 | 562 | end |
|
563 | 563 | |
|
564 | 564 | css << 'project-' + @project.identifier if @project && @project.identifier.present? |
|
565 | 565 | css << 'controller-' + controller_name |
|
566 | 566 | css << 'action-' + action_name |
|
567 | 567 | if UserPreference::TEXTAREA_FONT_OPTIONS.include?(User.current.pref.textarea_font) |
|
568 | 568 | css << "textarea-#{User.current.pref.textarea_font}" |
|
569 | 569 | end |
|
570 | 570 | css.join(' ') |
|
571 | 571 | end |
|
572 | 572 | |
|
573 | 573 | def accesskey(s) |
|
574 | 574 | @used_accesskeys ||= [] |
|
575 | 575 | key = Redmine::AccessKeys.key_for(s) |
|
576 | 576 | return nil if @used_accesskeys.include?(key) |
|
577 | 577 | @used_accesskeys << key |
|
578 | 578 | key |
|
579 | 579 | end |
|
580 | 580 | |
|
581 | 581 | # Formats text according to system settings. |
|
582 | 582 | # 2 ways to call this method: |
|
583 | 583 | # * with a String: textilizable(text, options) |
|
584 | 584 | # * with an object and one of its attribute: textilizable(issue, :description, options) |
|
585 | 585 | def textilizable(*args) |
|
586 | 586 | options = args.last.is_a?(Hash) ? args.pop : {} |
|
587 | 587 | case args.size |
|
588 | 588 | when 1 |
|
589 | 589 | obj = options[:object] |
|
590 | 590 | text = args.shift |
|
591 | 591 | when 2 |
|
592 | 592 | obj = args.shift |
|
593 | 593 | attr = args.shift |
|
594 | 594 | text = obj.send(attr).to_s |
|
595 | 595 | else |
|
596 | 596 | raise ArgumentError, 'invalid arguments to textilizable' |
|
597 | 597 | end |
|
598 | 598 | return '' if text.blank? |
|
599 | 599 | project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil) |
|
600 | 600 | @only_path = only_path = options.delete(:only_path) == false ? false : true |
|
601 | 601 | |
|
602 | 602 | text = text.dup |
|
603 | 603 | macros = catch_macros(text) |
|
604 | 604 | text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) |
|
605 | 605 | |
|
606 | 606 | @parsed_headings = [] |
|
607 | 607 | @heading_anchors = {} |
|
608 | 608 | @current_section = 0 if options[:edit_section_links] |
|
609 | 609 | |
|
610 | 610 | parse_sections(text, project, obj, attr, only_path, options) |
|
611 | 611 | text = parse_non_pre_blocks(text, obj, macros) do |text| |
|
612 | 612 | [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links].each do |method_name| |
|
613 | 613 | send method_name, text, project, obj, attr, only_path, options |
|
614 | 614 | end |
|
615 | 615 | end |
|
616 | 616 | parse_headings(text, project, obj, attr, only_path, options) |
|
617 | 617 | |
|
618 | 618 | if @parsed_headings.any? |
|
619 | 619 | replace_toc(text, @parsed_headings) |
|
620 | 620 | end |
|
621 | 621 | |
|
622 | 622 | text.html_safe |
|
623 | 623 | end |
|
624 | 624 | |
|
625 | 625 | def parse_non_pre_blocks(text, obj, macros) |
|
626 | 626 | s = StringScanner.new(text) |
|
627 | 627 | tags = [] |
|
628 | 628 | parsed = '' |
|
629 | 629 | while !s.eos? |
|
630 | 630 | s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im) |
|
631 | 631 | text, full_tag, closing, tag = s[1], s[2], s[3], s[4] |
|
632 | 632 | if tags.empty? |
|
633 | 633 | yield text |
|
634 | 634 | inject_macros(text, obj, macros) if macros.any? |
|
635 | 635 | else |
|
636 | 636 | inject_macros(text, obj, macros, false) if macros.any? |
|
637 | 637 | end |
|
638 | 638 | parsed << text |
|
639 | 639 | if tag |
|
640 | 640 | if closing |
|
641 | 641 | if tags.last && tags.last.casecmp(tag) == 0 |
|
642 | 642 | tags.pop |
|
643 | 643 | end |
|
644 | 644 | else |
|
645 | 645 | tags << tag.downcase |
|
646 | 646 | end |
|
647 | 647 | parsed << full_tag |
|
648 | 648 | end |
|
649 | 649 | end |
|
650 | 650 | # Close any non closing tags |
|
651 | 651 | while tag = tags.pop |
|
652 | 652 | parsed << "</#{tag}>" |
|
653 | 653 | end |
|
654 | 654 | parsed |
|
655 | 655 | end |
|
656 | 656 | |
|
657 | 657 | def parse_inline_attachments(text, project, obj, attr, only_path, options) |
|
658 | 658 | return if options[:inline_attachments] == false |
|
659 | 659 | |
|
660 | 660 | # when using an image link, try to use an attachment, if possible |
|
661 | 661 | attachments = options[:attachments] || [] |
|
662 | 662 | attachments += obj.attachments if obj.respond_to?(:attachments) |
|
663 | 663 | if attachments.present? |
|
664 | 664 | text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m| |
|
665 | 665 | filename, ext, alt, alttext = $1.downcase, $2, $3, $4 |
|
666 | 666 | # search for the picture in attachments |
|
667 | 667 | if found = Attachment.latest_attach(attachments, CGI.unescape(filename)) |
|
668 | 668 | image_url = download_named_attachment_url(found, found.filename, :only_path => only_path) |
|
669 | 669 | desc = found.description.to_s.gsub('"', '') |
|
670 | 670 | if !desc.blank? && alttext.blank? |
|
671 | 671 | alt = " title=\"#{desc}\" alt=\"#{desc}\"" |
|
672 | 672 | end |
|
673 | 673 | "src=\"#{image_url}\"#{alt}" |
|
674 | 674 | else |
|
675 | 675 | m |
|
676 | 676 | end |
|
677 | 677 | end |
|
678 | 678 | end |
|
679 | 679 | end |
|
680 | 680 | |
|
681 | 681 | # Wiki links |
|
682 | 682 | # |
|
683 | 683 | # Examples: |
|
684 | 684 | # [[mypage]] |
|
685 | 685 | # [[mypage|mytext]] |
|
686 | 686 | # wiki links can refer other project wikis, using project name or identifier: |
|
687 | 687 | # [[project:]] -> wiki starting page |
|
688 | 688 | # [[project:|mytext]] |
|
689 | 689 | # [[project:mypage]] |
|
690 | 690 | # [[project:mypage|mytext]] |
|
691 | 691 | def parse_wiki_links(text, project, obj, attr, only_path, options) |
|
692 | 692 | text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m| |
|
693 | 693 | link_project = project |
|
694 | 694 | esc, all, page, title = $1, $2, $3, $5 |
|
695 | 695 | if esc.nil? |
|
696 | 696 | if page =~ /^([^\:]+)\:(.*)$/ |
|
697 | 697 | identifier, page = $1, $2 |
|
698 | 698 | link_project = Project.find_by_identifier(identifier) || Project.find_by_name(identifier) |
|
699 | 699 | title ||= identifier if page.blank? |
|
700 | 700 | end |
|
701 | 701 | |
|
702 | 702 | if link_project && link_project.wiki |
|
703 | 703 | # extract anchor |
|
704 | 704 | anchor = nil |
|
705 | 705 | if page =~ /^(.+?)\#(.+)$/ |
|
706 | 706 | page, anchor = $1, $2 |
|
707 | 707 | end |
|
708 | 708 | anchor = sanitize_anchor_name(anchor) if anchor.present? |
|
709 | 709 | # check if page exists |
|
710 | 710 | wiki_page = link_project.wiki.find_page(page) |
|
711 | 711 | url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page |
|
712 | 712 | "##{anchor}" |
|
713 | 713 | else |
|
714 | 714 | case options[:wiki_links] |
|
715 | 715 | when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '') |
|
716 | 716 | when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export |
|
717 | 717 | else |
|
718 | 718 | wiki_page_id = page.present? ? Wiki.titleize(page) : nil |
|
719 | 719 | parent = wiki_page.nil? && obj.is_a?(WikiContent) && obj.page && project == link_project ? obj.page.title : nil |
|
720 | 720 | url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, |
|
721 | 721 | :id => wiki_page_id, :version => nil, :anchor => anchor, :parent => parent) |
|
722 | 722 | end |
|
723 | 723 | end |
|
724 | 724 | link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new'))) |
|
725 | 725 | else |
|
726 | 726 | # project or wiki doesn't exist |
|
727 | 727 | all |
|
728 | 728 | end |
|
729 | 729 | else |
|
730 | 730 | all |
|
731 | 731 | end |
|
732 | 732 | end |
|
733 | 733 | end |
|
734 | 734 | |
|
735 | 735 | # Redmine links |
|
736 | 736 | # |
|
737 | 737 | # Examples: |
|
738 | 738 | # Issues: |
|
739 | 739 | # #52 -> Link to issue #52 |
|
740 | 740 | # Changesets: |
|
741 | 741 | # r52 -> Link to revision 52 |
|
742 | 742 | # commit:a85130f -> Link to scmid starting with a85130f |
|
743 | 743 | # Documents: |
|
744 | 744 | # document#17 -> Link to document with id 17 |
|
745 | 745 | # document:Greetings -> Link to the document with title "Greetings" |
|
746 | 746 | # document:"Some document" -> Link to the document with title "Some document" |
|
747 | 747 | # Versions: |
|
748 | 748 | # version#3 -> Link to version with id 3 |
|
749 | 749 | # version:1.0.0 -> Link to version named "1.0.0" |
|
750 | 750 | # version:"1.0 beta 2" -> Link to version named "1.0 beta 2" |
|
751 | 751 | # Attachments: |
|
752 | 752 | # attachment:file.zip -> Link to the attachment of the current object named file.zip |
|
753 | 753 | # Source files: |
|
754 | 754 | # source:some/file -> Link to the file located at /some/file in the project's repository |
|
755 | 755 | # source:some/file@52 -> Link to the file's revision 52 |
|
756 | 756 | # source:some/file#L120 -> Link to line 120 of the file |
|
757 | 757 | # source:some/file@52#L120 -> Link to line 120 of the file's revision 52 |
|
758 | 758 | # export:some/file -> Force the download of the file |
|
759 | 759 | # Forum messages: |
|
760 | 760 | # message#1218 -> Link to message with id 1218 |
|
761 | 761 | # Projects: |
|
762 | 762 | # project:someproject -> Link to project named "someproject" |
|
763 | 763 | # project#3 -> Link to project with id 3 |
|
764 | 764 | # |
|
765 | 765 | # Links can refer other objects from other projects, using project identifier: |
|
766 | 766 | # identifier:r52 |
|
767 | 767 | # identifier:document:"Some document" |
|
768 | 768 | # identifier:version:1.0.0 |
|
769 | 769 | # identifier:source:some/file |
|
770 | 770 | def parse_redmine_links(text, default_project, obj, attr, only_path, options) |
|
771 | 771 | text.gsub!(%r{<a( [^>]+?)?>(.*?)</a>|([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-_]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m| |
|
772 | 772 | tag_content, leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $2, $3, $4, $5, $6, $7, $12, $13, $10 || $14 || $20, $16 || $21, $17, $19 |
|
773 | 773 | if tag_content |
|
774 | 774 | $& |
|
775 | 775 | else |
|
776 | 776 | link = nil |
|
777 | 777 | project = default_project |
|
778 | 778 | if project_identifier |
|
779 | 779 | project = Project.visible.find_by_identifier(project_identifier) |
|
780 | 780 | end |
|
781 | 781 | if esc.nil? |
|
782 | 782 | if prefix.nil? && sep == 'r' |
|
783 | 783 | if project |
|
784 | 784 | repository = nil |
|
785 | 785 | if repo_identifier |
|
786 | 786 | repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} |
|
787 | 787 | else |
|
788 | 788 | repository = project.repository |
|
789 | 789 | end |
|
790 | 790 | # project.changesets.visible raises an SQL error because of a double join on repositories |
|
791 | 791 | if repository && |
|
792 | 792 | (changeset = Changeset.visible. |
|
793 | 793 | find_by_repository_id_and_revision(repository.id, identifier)) |
|
794 | 794 | link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"), |
|
795 | 795 | {:only_path => only_path, :controller => 'repositories', |
|
796 | 796 | :action => 'revision', :id => project, |
|
797 | 797 | :repository_id => repository.identifier_param, |
|
798 | 798 | :rev => changeset.revision}, |
|
799 | 799 | :class => 'changeset', |
|
800 | 800 | :title => truncate_single_line_raw(changeset.comments, 100)) |
|
801 | 801 | end |
|
802 | 802 | end |
|
803 | 803 | elsif sep == '#' |
|
804 | 804 | oid = identifier.to_i |
|
805 | 805 | case prefix |
|
806 | 806 | when nil |
|
807 | 807 | if oid.to_s == identifier && |
|
808 | 808 | issue = Issue.visible.find_by_id(oid) |
|
809 | 809 | anchor = comment_id ? "note-#{comment_id}" : nil |
|
810 | 810 | link = link_to("##{oid}#{comment_suffix}", |
|
811 | 811 | issue_url(issue, :only_path => only_path, :anchor => anchor), |
|
812 | 812 | :class => issue.css_classes, |
|
813 | 813 | :title => "#{issue.tracker.name}: #{issue.subject.truncate(100)} (#{issue.status.name})") |
|
814 | 814 | end |
|
815 | 815 | when 'document' |
|
816 | 816 | if document = Document.visible.find_by_id(oid) |
|
817 | 817 | link = link_to(document.title, document_url(document, :only_path => only_path), :class => 'document') |
|
818 | 818 | end |
|
819 | 819 | when 'version' |
|
820 | 820 | if version = Version.visible.find_by_id(oid) |
|
821 | 821 | link = link_to(version.name, version_url(version, :only_path => only_path), :class => 'version') |
|
822 | 822 | end |
|
823 | 823 | when 'message' |
|
824 | 824 | if message = Message.visible.find_by_id(oid) |
|
825 | 825 | link = link_to_message(message, {:only_path => only_path}, :class => 'message') |
|
826 | 826 | end |
|
827 | 827 | when 'forum' |
|
828 | 828 | if board = Board.visible.find_by_id(oid) |
|
829 | 829 | link = link_to(board.name, project_board_url(board.project, board, :only_path => only_path), :class => 'board') |
|
830 | 830 | end |
|
831 | 831 | when 'news' |
|
832 | 832 | if news = News.visible.find_by_id(oid) |
|
833 | 833 | link = link_to(news.title, news_url(news, :only_path => only_path), :class => 'news') |
|
834 | 834 | end |
|
835 | 835 | when 'project' |
|
836 | 836 | if p = Project.visible.find_by_id(oid) |
|
837 | 837 | link = link_to_project(p, {:only_path => only_path}, :class => 'project') |
|
838 | 838 | end |
|
839 | 839 | end |
|
840 | 840 | elsif sep == ':' |
|
841 | 841 | # removes the double quotes if any |
|
842 | 842 | name = identifier.gsub(%r{^"(.*)"$}, "\\1") |
|
843 | 843 | name = CGI.unescapeHTML(name) |
|
844 | 844 | case prefix |
|
845 | 845 | when 'document' |
|
846 | 846 | if project && document = project.documents.visible.find_by_title(name) |
|
847 | 847 | link = link_to(document.title, document_url(document, :only_path => only_path), :class => 'document') |
|
848 | 848 | end |
|
849 | 849 | when 'version' |
|
850 | 850 | if project && version = project.versions.visible.find_by_name(name) |
|
851 | 851 | link = link_to(version.name, version_url(version, :only_path => only_path), :class => 'version') |
|
852 | 852 | end |
|
853 | 853 | when 'forum' |
|
854 | 854 | if project && board = project.boards.visible.find_by_name(name) |
|
855 | 855 | link = link_to(board.name, project_board_url(board.project, board, :only_path => only_path), :class => 'board') |
|
856 | 856 | end |
|
857 | 857 | when 'news' |
|
858 | 858 | if project && news = project.news.visible.find_by_title(name) |
|
859 | 859 | link = link_to(news.title, news_url(news, :only_path => only_path), :class => 'news') |
|
860 | 860 | end |
|
861 | 861 | when 'commit', 'source', 'export' |
|
862 | 862 | if project |
|
863 | 863 | repository = nil |
|
864 | 864 | if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$} |
|
865 | 865 | repo_prefix, repo_identifier, name = $1, $2, $3 |
|
866 | 866 | repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} |
|
867 | 867 | else |
|
868 | 868 | repository = project.repository |
|
869 | 869 | end |
|
870 | 870 | if prefix == 'commit' |
|
871 | 871 | if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first) |
|
872 | 872 | link = link_to h("#{project_prefix}#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.identifier}, |
|
873 | 873 | :class => 'changeset', |
|
874 | 874 | :title => truncate_single_line_raw(changeset.comments, 100) |
|
875 | 875 | end |
|
876 | 876 | else |
|
877 | 877 | if repository && User.current.allowed_to?(:browse_repository, project) |
|
878 | 878 | name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$} |
|
879 | 879 | path, rev, anchor = $1, $3, $5 |
|
880 | 880 | link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param, |
|
881 | 881 | :path => to_path_param(path), |
|
882 | 882 | :rev => rev, |
|
883 | 883 | :anchor => anchor}, |
|
884 | 884 | :class => (prefix == 'export' ? 'source download' : 'source') |
|
885 | 885 | end |
|
886 | 886 | end |
|
887 | 887 | repo_prefix = nil |
|
888 | 888 | end |
|
889 | 889 | when 'attachment' |
|
890 | 890 | attachments = options[:attachments] || [] |
|
891 | 891 | attachments += obj.attachments if obj.respond_to?(:attachments) |
|
892 | 892 | if attachments && attachment = Attachment.latest_attach(attachments, name) |
|
893 | 893 | link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment') |
|
894 | 894 | end |
|
895 | 895 | when 'project' |
|
896 | 896 | if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first |
|
897 | 897 | link = link_to_project(p, {:only_path => only_path}, :class => 'project') |
|
898 | 898 | end |
|
899 | 899 | end |
|
900 | 900 | end |
|
901 | 901 | end |
|
902 | 902 | (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}")) |
|
903 | 903 | end |
|
904 | 904 | end |
|
905 | 905 | end |
|
906 | 906 | |
|
907 | 907 | HEADING_RE = /(<h(\d)( [^>]+)?>(.+?)<\/h(\d)>)/i unless const_defined?(:HEADING_RE) |
|
908 | 908 | |
|
909 | 909 | def parse_sections(text, project, obj, attr, only_path, options) |
|
910 | 910 | return unless options[:edit_section_links] |
|
911 | 911 | text.gsub!(HEADING_RE) do |
|
912 | 912 | heading, level = $1, $2 |
|
913 | 913 | @current_section += 1 |
|
914 | 914 | if @current_section > 1 |
|
915 | 915 | content_tag('div', |
|
916 | 916 | link_to(l(:button_edit_section), options[:edit_section_links].merge(:section => @current_section), |
|
917 | 917 | :class => 'icon-only icon-edit'), |
|
918 | 918 | :class => "contextual heading-#{level}", |
|
919 | 919 | :title => l(:button_edit_section), |
|
920 | 920 | :id => "section-#{@current_section}") + heading.html_safe |
|
921 | 921 | else |
|
922 | 922 | heading |
|
923 | 923 | end |
|
924 | 924 | end |
|
925 | 925 | end |
|
926 | 926 | |
|
927 | 927 | # Headings and TOC |
|
928 | 928 | # Adds ids and links to headings unless options[:headings] is set to false |
|
929 | 929 | def parse_headings(text, project, obj, attr, only_path, options) |
|
930 | 930 | return if options[:headings] == false |
|
931 | 931 | |
|
932 | 932 | text.gsub!(HEADING_RE) do |
|
933 | 933 | level, attrs, content = $2.to_i, $3, $4 |
|
934 | 934 | item = strip_tags(content).strip |
|
935 | 935 | anchor = sanitize_anchor_name(item) |
|
936 | 936 | # used for single-file wiki export |
|
937 | 937 | anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) |
|
938 | 938 | @heading_anchors[anchor] ||= 0 |
|
939 | 939 | idx = (@heading_anchors[anchor] += 1) |
|
940 | 940 | if idx > 1 |
|
941 | 941 | anchor = "#{anchor}-#{idx}" |
|
942 | 942 | end |
|
943 | 943 | @parsed_headings << [level, anchor, item] |
|
944 | 944 | "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">¶</a></h#{level}>" |
|
945 | 945 | end |
|
946 | 946 | end |
|
947 | 947 | |
|
948 | 948 | MACROS_RE = /( |
|
949 | 949 | (!)? # escaping |
|
950 | 950 | ( |
|
951 | 951 | \{\{ # opening tag |
|
952 | 952 | ([\w]+) # macro name |
|
953 | 953 | (\(([^\n\r]*?)\))? # optional arguments |
|
954 | 954 | ([\n\r].*?[\n\r])? # optional block of text |
|
955 | 955 | \}\} # closing tag |
|
956 | 956 | ) |
|
957 | 957 | )/mx unless const_defined?(:MACROS_RE) |
|
958 | 958 | |
|
959 | 959 | MACRO_SUB_RE = /( |
|
960 | 960 | \{\{ |
|
961 | 961 | macro\((\d+)\) |
|
962 | 962 | \}\} |
|
963 | 963 | )/x unless const_defined?(:MACRO_SUB_RE) |
|
964 | 964 | |
|
965 | 965 | # Extracts macros from text |
|
966 | 966 | def catch_macros(text) |
|
967 | 967 | macros = {} |
|
968 | 968 | text.gsub!(MACROS_RE) do |
|
969 | 969 | all, macro = $1, $4.downcase |
|
970 | 970 | if macro_exists?(macro) || all =~ MACRO_SUB_RE |
|
971 | 971 | index = macros.size |
|
972 | 972 | macros[index] = all |
|
973 | 973 | "{{macro(#{index})}}" |
|
974 | 974 | else |
|
975 | 975 | all |
|
976 | 976 | end |
|
977 | 977 | end |
|
978 | 978 | macros |
|
979 | 979 | end |
|
980 | 980 | |
|
981 | 981 | # Executes and replaces macros in text |
|
982 | 982 | def inject_macros(text, obj, macros, execute=true) |
|
983 | 983 | text.gsub!(MACRO_SUB_RE) do |
|
984 | 984 | all, index = $1, $2.to_i |
|
985 | 985 | orig = macros.delete(index) |
|
986 | 986 | if execute && orig && orig =~ MACROS_RE |
|
987 | 987 | esc, all, macro, args, block = $2, $3, $4.downcase, $6.to_s, $7.try(:strip) |
|
988 | 988 | if esc.nil? |
|
989 | 989 | h(exec_macro(macro, obj, args, block) || all) |
|
990 | 990 | else |
|
991 | 991 | h(all) |
|
992 | 992 | end |
|
993 | 993 | elsif orig |
|
994 | 994 | h(orig) |
|
995 | 995 | else |
|
996 | 996 | h(all) |
|
997 | 997 | end |
|
998 | 998 | end |
|
999 | 999 | end |
|
1000 | 1000 | |
|
1001 | 1001 | TOC_RE = /<p>\{\{((<|<)|(>|>))?toc\}\}<\/p>/i unless const_defined?(:TOC_RE) |
|
1002 | 1002 | |
|
1003 | 1003 | # Renders the TOC with given headings |
|
1004 | 1004 | def replace_toc(text, headings) |
|
1005 | 1005 | text.gsub!(TOC_RE) do |
|
1006 | 1006 | left_align, right_align = $2, $3 |
|
1007 | 1007 | # Keep only the 4 first levels |
|
1008 | 1008 | headings = headings.select{|level, anchor, item| level <= 4} |
|
1009 | 1009 | if headings.empty? |
|
1010 | 1010 | '' |
|
1011 | 1011 | else |
|
1012 | 1012 | div_class = 'toc' |
|
1013 | 1013 | div_class << ' right' if right_align |
|
1014 | 1014 | div_class << ' left' if left_align |
|
1015 | 1015 | out = "<ul class=\"#{div_class}\"><li>" |
|
1016 | 1016 | root = headings.map(&:first).min |
|
1017 | 1017 | current = root |
|
1018 | 1018 | started = false |
|
1019 | 1019 | headings.each do |level, anchor, item| |
|
1020 | 1020 | if level > current |
|
1021 | 1021 | out << '<ul><li>' * (level - current) |
|
1022 | 1022 | elsif level < current |
|
1023 | 1023 | out << "</li></ul>\n" * (current - level) + "</li><li>" |
|
1024 | 1024 | elsif started |
|
1025 | 1025 | out << '</li><li>' |
|
1026 | 1026 | end |
|
1027 | 1027 | out << "<a href=\"##{anchor}\">#{item}</a>" |
|
1028 | 1028 | current = level |
|
1029 | 1029 | started = true |
|
1030 | 1030 | end |
|
1031 | 1031 | out << '</li></ul>' * (current - root) |
|
1032 | 1032 | out << '</li></ul>' |
|
1033 | 1033 | end |
|
1034 | 1034 | end |
|
1035 | 1035 | end |
|
1036 | 1036 | |
|
1037 | 1037 | # Same as Rails' simple_format helper without using paragraphs |
|
1038 | 1038 | def simple_format_without_paragraph(text) |
|
1039 | 1039 | text.to_s. |
|
1040 | 1040 | gsub(/\r\n?/, "\n"). # \r\n and \r -> \n |
|
1041 | 1041 | gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br |
|
1042 | 1042 | gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br |
|
1043 | 1043 | html_safe |
|
1044 | 1044 | end |
|
1045 | 1045 | |
|
1046 | 1046 | def lang_options_for_select(blank=true) |
|
1047 | 1047 | (blank ? [["(auto)", ""]] : []) + languages_options |
|
1048 | 1048 | end |
|
1049 | 1049 | |
|
1050 | 1050 | def labelled_form_for(*args, &proc) |
|
1051 | 1051 | args << {} unless args.last.is_a?(Hash) |
|
1052 | 1052 | options = args.last |
|
1053 | 1053 | if args.first.is_a?(Symbol) |
|
1054 | 1054 | options.merge!(:as => args.shift) |
|
1055 | 1055 | end |
|
1056 | 1056 | options.merge!({:builder => Redmine::Views::LabelledFormBuilder}) |
|
1057 | 1057 | form_for(*args, &proc) |
|
1058 | 1058 | end |
|
1059 | 1059 | |
|
1060 | 1060 | def labelled_fields_for(*args, &proc) |
|
1061 | 1061 | args << {} unless args.last.is_a?(Hash) |
|
1062 | 1062 | options = args.last |
|
1063 | 1063 | options.merge!({:builder => Redmine::Views::LabelledFormBuilder}) |
|
1064 | 1064 | fields_for(*args, &proc) |
|
1065 | 1065 | end |
|
1066 | 1066 | |
|
1067 | 1067 | # Render the error messages for the given objects |
|
1068 | 1068 | def error_messages_for(*objects) |
|
1069 | 1069 | objects = objects.map {|o| o.is_a?(String) ? instance_variable_get("@#{o}") : o}.compact |
|
1070 | 1070 | errors = objects.map {|o| o.errors.full_messages}.flatten |
|
1071 | 1071 | render_error_messages(errors) |
|
1072 | 1072 | end |
|
1073 | 1073 | |
|
1074 | 1074 | # Renders a list of error messages |
|
1075 | 1075 | def render_error_messages(errors) |
|
1076 | 1076 | html = "" |
|
1077 | 1077 | if errors.present? |
|
1078 | 1078 | html << "<div id='errorExplanation'><ul>\n" |
|
1079 | 1079 | errors.each do |error| |
|
1080 | 1080 | html << "<li>#{h error}</li>\n" |
|
1081 | 1081 | end |
|
1082 | 1082 | html << "</ul></div>\n" |
|
1083 | 1083 | end |
|
1084 | 1084 | html.html_safe |
|
1085 | 1085 | end |
|
1086 | 1086 | |
|
1087 | 1087 | def delete_link(url, options={}) |
|
1088 | 1088 | options = { |
|
1089 | 1089 | :method => :delete, |
|
1090 | 1090 | :data => {:confirm => l(:text_are_you_sure)}, |
|
1091 | 1091 | :class => 'icon icon-del' |
|
1092 | 1092 | }.merge(options) |
|
1093 | 1093 | |
|
1094 | 1094 | link_to l(:button_delete), url, options |
|
1095 | 1095 | end |
|
1096 | 1096 | |
|
1097 | 1097 | def preview_link(url, form, target='preview', options={}) |
|
1098 | 1098 | content_tag 'a', l(:label_preview), { |
|
1099 | 1099 | :href => "#", |
|
1100 | 1100 | :onclick => %|submitPreview("#{escape_javascript url_for(url)}", "#{escape_javascript form}", "#{escape_javascript target}"); return false;|, |
|
1101 | 1101 | :accesskey => accesskey(:preview) |
|
1102 | 1102 | }.merge(options) |
|
1103 | 1103 | end |
|
1104 | 1104 | |
|
1105 | 1105 | def link_to_function(name, function, html_options={}) |
|
1106 | 1106 | content_tag(:a, name, {:href => '#', :onclick => "#{function}; return false;"}.merge(html_options)) |
|
1107 | 1107 | end |
|
1108 | 1108 | |
|
1109 | 1109 | # Helper to render JSON in views |
|
1110 | 1110 | def raw_json(arg) |
|
1111 | 1111 | arg.to_json.to_s.gsub('/', '\/').html_safe |
|
1112 | 1112 | end |
|
1113 | 1113 | |
|
1114 | 1114 | def back_url |
|
1115 | 1115 | url = params[:back_url] |
|
1116 | 1116 | if url.nil? && referer = request.env['HTTP_REFERER'] |
|
1117 | 1117 | url = CGI.unescape(referer.to_s) |
|
1118 | 1118 | # URLs that contains the utf8=[checkmark] parameter added by Rails are |
|
1119 | 1119 | # parsed as invalid by URI.parse so the redirect to the back URL would |
|
1120 | 1120 | # not be accepted (ApplicationController#validate_back_url would return |
|
1121 | 1121 | # false) |
|
1122 | 1122 | url.gsub!(/(\?|&)utf8=\u2713&?/, '\1') |
|
1123 | 1123 | end |
|
1124 | 1124 | url |
|
1125 | 1125 | end |
|
1126 | 1126 | |
|
1127 | 1127 | def back_url_hidden_field_tag |
|
1128 | 1128 | url = back_url |
|
1129 | 1129 | hidden_field_tag('back_url', url, :id => nil) unless url.blank? |
|
1130 | 1130 | end |
|
1131 | 1131 | |
|
1132 | 1132 | def check_all_links(form_name) |
|
1133 | 1133 | link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") + |
|
1134 | 1134 | " | ".html_safe + |
|
1135 | 1135 | link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)") |
|
1136 | 1136 | end |
|
1137 | 1137 | |
|
1138 | 1138 | def toggle_checkboxes_link(selector) |
|
1139 | 1139 | link_to_function '', |
|
1140 | 1140 | "toggleCheckboxesBySelector('#{selector}')", |
|
1141 | 1141 | :title => "#{l(:button_check_all)} / #{l(:button_uncheck_all)}", |
|
1142 | 1142 | :class => 'toggle-checkboxes' |
|
1143 | 1143 | end |
|
1144 | 1144 | |
|
1145 | 1145 | def progress_bar(pcts, options={}) |
|
1146 | 1146 | pcts = [pcts, pcts] unless pcts.is_a?(Array) |
|
1147 | 1147 | pcts = pcts.collect(&:round) |
|
1148 | 1148 | pcts[1] = pcts[1] - pcts[0] |
|
1149 | 1149 | pcts << (100 - pcts[1] - pcts[0]) |
|
1150 | 1150 | titles = options[:titles].to_a |
|
1151 | 1151 | titles[0] = "#{pcts[0]}%" if titles[0].blank? |
|
1152 | 1152 | legend = options[:legend] || '' |
|
1153 | 1153 | content_tag('table', |
|
1154 | 1154 | content_tag('tr', |
|
1155 | 1155 | (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed', :title => titles[0]) : ''.html_safe) + |
|
1156 | 1156 | (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done', :title => titles[1]) : ''.html_safe) + |
|
1157 | 1157 | (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo', :title => titles[2]) : ''.html_safe) |
|
1158 | 1158 | ), :class => "progress progress-#{pcts[0]}").html_safe + |
|
1159 | 1159 | content_tag('p', legend, :class => 'percent').html_safe |
|
1160 | 1160 | end |
|
1161 | 1161 | |
|
1162 | 1162 | def checked_image(checked=true) |
|
1163 | 1163 | if checked |
|
1164 | 1164 | @checked_image_tag ||= content_tag(:span, nil, :class => 'icon-only icon-checked') |
|
1165 | 1165 | end |
|
1166 | 1166 | end |
|
1167 | 1167 | |
|
1168 | 1168 | def context_menu |
|
1169 | 1169 | unless @context_menu_included |
|
1170 | 1170 | content_for :header_tags do |
|
1171 | 1171 | javascript_include_tag('context_menu') + |
|
1172 | 1172 | stylesheet_link_tag('context_menu') |
|
1173 | 1173 | end |
|
1174 | 1174 | if l(:direction) == 'rtl' |
|
1175 | 1175 | content_for :header_tags do |
|
1176 | 1176 | stylesheet_link_tag('context_menu_rtl') |
|
1177 | 1177 | end |
|
1178 | 1178 | end |
|
1179 | 1179 | @context_menu_included = true |
|
1180 | 1180 | end |
|
1181 | 1181 | nil |
|
1182 | 1182 | end |
|
1183 | 1183 | |
|
1184 | 1184 | def calendar_for(field_id) |
|
1185 | 1185 | include_calendar_headers_tags |
|
1186 | 1186 | javascript_tag("$(function() { $('##{field_id}').addClass('date').datepickerFallback(datepickerOptions); });") |
|
1187 | 1187 | end |
|
1188 | 1188 | |
|
1189 | 1189 | def include_calendar_headers_tags |
|
1190 | 1190 | unless @calendar_headers_tags_included |
|
1191 | 1191 | tags = ''.html_safe |
|
1192 | 1192 | @calendar_headers_tags_included = true |
|
1193 | 1193 | content_for :header_tags do |
|
1194 | 1194 | start_of_week = Setting.start_of_week |
|
1195 | 1195 | start_of_week = l(:general_first_day_of_week, :default => '1') if start_of_week.blank? |
|
1196 | 1196 | # Redmine uses 1..7 (monday..sunday) in settings and locales |
|
1197 | 1197 | # JQuery uses 0..6 (sunday..saturday), 7 needs to be changed to 0 |
|
1198 | 1198 | start_of_week = start_of_week.to_i % 7 |
|
1199 | 1199 | tags << javascript_tag( |
|
1200 | 1200 | "var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " + |
|
1201 | 1201 | "showOn: 'button', buttonImageOnly: true, buttonImage: '" + |
|
1202 | 1202 | path_to_image('/images/calendar.png') + |
|
1203 | 1203 | "', showButtonPanel: true, showWeek: true, showOtherMonths: true, " + |
|
1204 | 1204 | "selectOtherMonths: true, changeMonth: true, changeYear: true, " + |
|
1205 | 1205 | "beforeShow: beforeShowDatePicker};") |
|
1206 | 1206 | jquery_locale = l('jquery.locale', :default => current_language.to_s) |
|
1207 | 1207 | unless jquery_locale == 'en' |
|
1208 | 1208 | tags << javascript_include_tag("i18n/datepicker-#{jquery_locale}.js") |
|
1209 | 1209 | end |
|
1210 | 1210 | tags |
|
1211 | 1211 | end |
|
1212 | 1212 | end |
|
1213 | 1213 | end |
|
1214 | 1214 | |
|
1215 | 1215 | # Overrides Rails' stylesheet_link_tag with themes and plugins support. |
|
1216 | 1216 | # Examples: |
|
1217 | 1217 | # stylesheet_link_tag('styles') # => picks styles.css from the current theme or defaults |
|
1218 | 1218 | # stylesheet_link_tag('styles', :plugin => 'foo) # => picks styles.css from plugin's assets |
|
1219 | 1219 | # |
|
1220 | 1220 | def stylesheet_link_tag(*sources) |
|
1221 | 1221 | options = sources.last.is_a?(Hash) ? sources.pop : {} |
|
1222 | 1222 | plugin = options.delete(:plugin) |
|
1223 | 1223 | sources = sources.map do |source| |
|
1224 | 1224 | if plugin |
|
1225 | 1225 | "/plugin_assets/#{plugin}/stylesheets/#{source}" |
|
1226 | 1226 | elsif current_theme && current_theme.stylesheets.include?(source) |
|
1227 | 1227 | current_theme.stylesheet_path(source) |
|
1228 | 1228 | else |
|
1229 | 1229 | source |
|
1230 | 1230 | end |
|
1231 | 1231 | end |
|
1232 | 1232 | super *sources, options |
|
1233 | 1233 | end |
|
1234 | 1234 | |
|
1235 | 1235 | # Overrides Rails' image_tag with themes and plugins support. |
|
1236 | 1236 | # Examples: |
|
1237 | 1237 | # image_tag('image.png') # => picks image.png from the current theme or defaults |
|
1238 | 1238 | # image_tag('image.png', :plugin => 'foo) # => picks image.png from plugin's assets |
|
1239 | 1239 | # |
|
1240 | 1240 | def image_tag(source, options={}) |
|
1241 | 1241 | if plugin = options.delete(:plugin) |
|
1242 | 1242 | source = "/plugin_assets/#{plugin}/images/#{source}" |
|
1243 | 1243 | elsif current_theme && current_theme.images.include?(source) |
|
1244 | 1244 | source = current_theme.image_path(source) |
|
1245 | 1245 | end |
|
1246 | 1246 | super source, options |
|
1247 | 1247 | end |
|
1248 | 1248 | |
|
1249 | 1249 | # Overrides Rails' javascript_include_tag with plugins support |
|
1250 | 1250 | # Examples: |
|
1251 | 1251 | # javascript_include_tag('scripts') # => picks scripts.js from defaults |
|
1252 | 1252 | # javascript_include_tag('scripts', :plugin => 'foo) # => picks scripts.js from plugin's assets |
|
1253 | 1253 | # |
|
1254 | 1254 | def javascript_include_tag(*sources) |
|
1255 | 1255 | options = sources.last.is_a?(Hash) ? sources.pop : {} |
|
1256 | 1256 | if plugin = options.delete(:plugin) |
|
1257 | 1257 | sources = sources.map do |source| |
|
1258 | 1258 | if plugin |
|
1259 | 1259 | "/plugin_assets/#{plugin}/javascripts/#{source}" |
|
1260 | 1260 | else |
|
1261 | 1261 | source |
|
1262 | 1262 | end |
|
1263 | 1263 | end |
|
1264 | 1264 | end |
|
1265 | 1265 | super *sources, options |
|
1266 | 1266 | end |
|
1267 | 1267 | |
|
1268 | 1268 | def sidebar_content? |
|
1269 | 1269 | content_for?(:sidebar) || view_layouts_base_sidebar_hook_response.present? |
|
1270 | 1270 | end |
|
1271 | 1271 | |
|
1272 | 1272 | def view_layouts_base_sidebar_hook_response |
|
1273 | 1273 | @view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar) |
|
1274 | 1274 | end |
|
1275 | 1275 | |
|
1276 | 1276 | def email_delivery_enabled? |
|
1277 | 1277 | !!ActionMailer::Base.perform_deliveries |
|
1278 | 1278 | end |
|
1279 | 1279 | |
|
1280 | 1280 | # Returns the avatar image tag for the given +user+ if avatars are enabled |
|
1281 | 1281 | # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>') |
|
1282 | 1282 | def avatar(user, options = { }) |
|
1283 | 1283 | if Setting.gravatar_enabled? |
|
1284 | 1284 | options.merge!(:default => Setting.gravatar_default) |
|
1285 | 1285 | email = nil |
|
1286 | 1286 | if user.respond_to?(:mail) |
|
1287 | 1287 | email = user.mail |
|
1288 | 1288 | elsif user.to_s =~ %r{<(.+?)>} |
|
1289 | 1289 | email = $1 |
|
1290 | 1290 | end |
|
1291 | 1291 | return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil |
|
1292 | 1292 | else |
|
1293 | 1293 | '' |
|
1294 | 1294 | end |
|
1295 | 1295 | end |
|
1296 | 1296 | |
|
1297 | 1297 | # Returns a link to edit user's avatar if avatars are enabled |
|
1298 | 1298 | def avatar_edit_link(user, options={}) |
|
1299 | 1299 | if Setting.gravatar_enabled? |
|
1300 | 1300 | url = "https://gravatar.com" |
|
1301 | 1301 | link_to avatar(user, {:title => l(:button_edit)}.merge(options)), url, :target => '_blank' |
|
1302 | 1302 | end |
|
1303 | 1303 | end |
|
1304 | 1304 | |
|
1305 | 1305 | def sanitize_anchor_name(anchor) |
|
1306 | 1306 | anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-') |
|
1307 | 1307 | end |
|
1308 | 1308 | |
|
1309 | 1309 | # Returns the javascript tags that are included in the html layout head |
|
1310 | 1310 | def javascript_heads |
|
1311 | 1311 | tags = javascript_include_tag('jquery-1.11.1-ui-1.11.0-ujs-3.1.4', 'application', 'responsive') |
|
1312 | 1312 | unless User.current.pref.warn_on_leaving_unsaved == '0' |
|
1313 | 1313 | tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });") |
|
1314 | 1314 | end |
|
1315 | 1315 | tags |
|
1316 | 1316 | end |
|
1317 | 1317 | |
|
1318 | 1318 | def favicon |
|
1319 | 1319 | "<link rel='shortcut icon' href='#{favicon_path}' />".html_safe |
|
1320 | 1320 | end |
|
1321 | 1321 | |
|
1322 | 1322 | # Returns the path to the favicon |
|
1323 | 1323 | def favicon_path |
|
1324 | 1324 | icon = (current_theme && current_theme.favicon?) ? current_theme.favicon_path : '/favicon.ico' |
|
1325 | 1325 | image_path(icon) |
|
1326 | 1326 | end |
|
1327 | 1327 | |
|
1328 | 1328 | # Returns the full URL to the favicon |
|
1329 | 1329 | def favicon_url |
|
1330 | 1330 | # TODO: use #image_url introduced in Rails4 |
|
1331 | 1331 | path = favicon_path |
|
1332 | 1332 | base = url_for(:controller => 'welcome', :action => 'index', :only_path => false) |
|
1333 | 1333 | base.sub(%r{/+$},'') + '/' + path.sub(%r{^/+},'') |
|
1334 | 1334 | end |
|
1335 | 1335 | |
|
1336 | 1336 | def robot_exclusion_tag |
|
1337 | 1337 | '<meta name="robots" content="noindex,follow,noarchive" />'.html_safe |
|
1338 | 1338 | end |
|
1339 | 1339 | |
|
1340 | 1340 | # Returns true if arg is expected in the API response |
|
1341 | 1341 | def include_in_api_response?(arg) |
|
1342 | 1342 | unless @included_in_api_response |
|
1343 | 1343 | param = params[:include] |
|
1344 | 1344 | @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',') |
|
1345 | 1345 | @included_in_api_response.collect!(&:strip) |
|
1346 | 1346 | end |
|
1347 | 1347 | @included_in_api_response.include?(arg.to_s) |
|
1348 | 1348 | end |
|
1349 | 1349 | |
|
1350 | 1350 | # Returns options or nil if nometa param or X-Redmine-Nometa header |
|
1351 | 1351 | # was set in the request |
|
1352 | 1352 | def api_meta(options) |
|
1353 | 1353 | if params[:nometa].present? || request.headers['X-Redmine-Nometa'] |
|
1354 | 1354 | # compatibility mode for activeresource clients that raise |
|
1355 | 1355 | # an error when deserializing an array with attributes |
|
1356 | 1356 | nil |
|
1357 | 1357 | else |
|
1358 | 1358 | options |
|
1359 | 1359 | end |
|
1360 | 1360 | end |
|
1361 | 1361 | |
|
1362 | 1362 | def generate_csv(&block) |
|
1363 | 1363 | decimal_separator = l(:general_csv_decimal_separator) |
|
1364 | 1364 | encoding = l(:general_csv_encoding) |
|
1365 | 1365 | end |
|
1366 | 1366 | |
|
1367 | 1367 | private |
|
1368 | 1368 | |
|
1369 | 1369 | def wiki_helper |
|
1370 | 1370 | helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting) |
|
1371 | 1371 | extend helper |
|
1372 | 1372 | return self |
|
1373 | 1373 | end |
|
1374 | 1374 | end |
@@ -1,348 +1,355 | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | # |
|
3 | 3 | # Redmine - project management software |
|
4 | 4 | # Copyright (C) 2006-2016 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 | module QueriesHelper |
|
21 | 21 | include ApplicationHelper |
|
22 | 22 | |
|
23 | 23 | def filters_options_for_select(query) |
|
24 | 24 | ungrouped = [] |
|
25 | 25 | grouped = {} |
|
26 | 26 | query.available_filters.map do |field, field_options| |
|
27 | 27 | if field_options[:type] == :relation |
|
28 | 28 | group = :label_relations |
|
29 | 29 | elsif field_options[:type] == :tree |
|
30 | 30 | group = query.is_a?(IssueQuery) ? :label_relations : nil |
|
31 | 31 | elsif field =~ /^(.+)\./ |
|
32 | 32 | # association filters |
|
33 | 33 | group = "field_#{$1}" |
|
34 | 34 | elsif %w(member_of_group assigned_to_role).include?(field) |
|
35 | 35 | group = :field_assigned_to |
|
36 | 36 | elsif field_options[:type] == :date_past || field_options[:type] == :date |
|
37 | 37 | group = :label_date |
|
38 | 38 | end |
|
39 | 39 | if group |
|
40 | 40 | (grouped[group] ||= []) << [field_options[:name], field] |
|
41 | 41 | else |
|
42 | 42 | ungrouped << [field_options[:name], field] |
|
43 | 43 | end |
|
44 | 44 | end |
|
45 | 45 | # Don't group dates if there's only one (eg. time entries filters) |
|
46 | 46 | if grouped[:label_date].try(:size) == 1 |
|
47 | 47 | ungrouped << grouped.delete(:label_date).first |
|
48 | 48 | end |
|
49 | 49 | s = options_for_select([[]] + ungrouped) |
|
50 | 50 | if grouped.present? |
|
51 | 51 | localized_grouped = grouped.map {|k,v| [l(k), v]} |
|
52 | 52 | s << grouped_options_for_select(localized_grouped) |
|
53 | 53 | end |
|
54 | 54 | s |
|
55 | 55 | end |
|
56 | 56 | |
|
57 | 57 | def query_filters_hidden_tags(query) |
|
58 | 58 | tags = ''.html_safe |
|
59 | 59 | query.filters.each do |field, options| |
|
60 | 60 | tags << hidden_field_tag("f[]", field, :id => nil) |
|
61 | 61 | tags << hidden_field_tag("op[#{field}]", options[:operator], :id => nil) |
|
62 | 62 | options[:values].each do |value| |
|
63 | 63 | tags << hidden_field_tag("v[#{field}][]", value, :id => nil) |
|
64 | 64 | end |
|
65 | 65 | end |
|
66 | 66 | tags |
|
67 | 67 | end |
|
68 | 68 | |
|
69 | 69 | def query_columns_hidden_tags(query) |
|
70 | 70 | tags = ''.html_safe |
|
71 | 71 | query.columns.each do |column| |
|
72 | 72 | tags << hidden_field_tag("c[]", column.name, :id => nil) |
|
73 | 73 | end |
|
74 | 74 | tags |
|
75 | 75 | end |
|
76 | 76 | |
|
77 | 77 | def query_hidden_tags(query) |
|
78 | 78 | query_filters_hidden_tags(query) + query_columns_hidden_tags(query) |
|
79 | 79 | end |
|
80 | 80 | |
|
81 | 81 | def group_by_column_select_tag(query) |
|
82 | 82 | options = [[]] + query.groupable_columns.collect {|c| [c.caption, c.name.to_s]} |
|
83 | 83 | select_tag('group_by', options_for_select(options, @query.group_by)) |
|
84 | 84 | end |
|
85 | 85 | |
|
86 | 86 | def available_block_columns_tags(query) |
|
87 | 87 | tags = ''.html_safe |
|
88 | 88 | query.available_block_columns.each do |column| |
|
89 | 89 | tags << content_tag('label', check_box_tag('c[]', column.name.to_s, query.has_column?(column), :id => nil) + " #{column.caption}", :class => 'inline') |
|
90 | 90 | end |
|
91 | 91 | tags |
|
92 | 92 | end |
|
93 | 93 | |
|
94 | 94 | def available_totalable_columns_tags(query) |
|
95 | 95 | tags = ''.html_safe |
|
96 | 96 | query.available_totalable_columns.each do |column| |
|
97 | 97 | tags << content_tag('label', check_box_tag('t[]', column.name.to_s, query.totalable_columns.include?(column), :id => nil) + " #{column.caption}", :class => 'inline') |
|
98 | 98 | end |
|
99 | 99 | tags << hidden_field_tag('t[]', '') |
|
100 | 100 | tags |
|
101 | 101 | end |
|
102 | 102 | |
|
103 | 103 | def query_available_inline_columns_options(query) |
|
104 | 104 | (query.available_inline_columns - query.columns).reject(&:frozen?).collect {|column| [column.caption, column.name]} |
|
105 | 105 | end |
|
106 | 106 | |
|
107 | 107 | def query_selected_inline_columns_options(query) |
|
108 | 108 | (query.inline_columns & query.available_inline_columns).reject(&:frozen?).collect {|column| [column.caption, column.name]} |
|
109 | 109 | end |
|
110 | 110 | |
|
111 | 111 | def render_query_columns_selection(query, options={}) |
|
112 | 112 | tag_name = (options[:name] || 'c') + '[]' |
|
113 | 113 | render :partial => 'queries/columns', :locals => {:query => query, :tag_name => tag_name} |
|
114 | 114 | end |
|
115 | 115 | |
|
116 | 116 | def grouped_query_results(items, query, item_count_by_group, &block) |
|
117 | 117 | previous_group, first = false, true |
|
118 | 118 | totals_by_group = query.totalable_columns.inject({}) do |h, column| |
|
119 | 119 | h[column] = query.total_by_group_for(column) |
|
120 | 120 | h |
|
121 | 121 | end |
|
122 | 122 | items.each do |item| |
|
123 | 123 | group_name = group_count = nil |
|
124 | 124 | if query.grouped? |
|
125 | 125 | group = query.group_by_column.value(item) |
|
126 | 126 | if first || group != previous_group |
|
127 | 127 | if group.blank? && group != false |
|
128 | 128 | group_name = "(#{l(:label_blank_value)})" |
|
129 | 129 | else |
|
130 | 130 | group_name = format_object(group) |
|
131 | 131 | end |
|
132 | 132 | group_name ||= "" |
|
133 | 133 | group_count = item_count_by_group ? item_count_by_group[group] : nil |
|
134 | 134 | group_totals = totals_by_group.map {|column, t| total_tag(column, t[group] || 0)}.join(" ").html_safe |
|
135 | 135 | end |
|
136 | 136 | end |
|
137 | 137 | yield item, group_name, group_count, group_totals |
|
138 | 138 | previous_group, first = group, false |
|
139 | 139 | end |
|
140 | 140 | end |
|
141 | 141 | |
|
142 | 142 | def render_query_totals(query) |
|
143 | 143 | return unless query.totalable_columns.present? |
|
144 | 144 | totals = query.totalable_columns.map do |column| |
|
145 | 145 | total_tag(column, query.total_for(column)) |
|
146 | 146 | end |
|
147 | 147 | content_tag('p', totals.join(" ").html_safe, :class => "query-totals") |
|
148 | 148 | end |
|
149 | 149 | |
|
150 | 150 | def total_tag(column, value) |
|
151 | 151 | label = content_tag('span', "#{column.caption}:") |
|
152 | value = content_tag('span', format_object(value), :class => 'value') | |
|
152 | value = if [:hours, :spent_hours, :total_spent_hours, :estimated_hours].include? column.name | |
|
153 | format_hours(value) | |
|
154 | else | |
|
155 | format_object(value) | |
|
156 | end | |
|
157 | value = content_tag('span', value, :class => 'value') | |
|
153 | 158 | content_tag('span', label + " " + value, :class => "total-for-#{column.name.to_s.dasherize}") |
|
154 | 159 | end |
|
155 | 160 | |
|
156 | 161 | def column_header(column) |
|
157 | 162 | column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption, |
|
158 | 163 | :default_order => column.default_order) : |
|
159 | 164 | content_tag('th', h(column.caption)) |
|
160 | 165 | end |
|
161 | 166 | |
|
162 | 167 | def column_content(column, issue) |
|
163 | 168 | value = column.value_object(issue) |
|
164 | 169 | if value.is_a?(Array) |
|
165 | 170 | value.collect {|v| column_value(column, issue, v)}.compact.join(', ').html_safe |
|
166 | 171 | else |
|
167 | 172 | column_value(column, issue, value) |
|
168 | 173 | end |
|
169 | 174 | end |
|
170 | 175 | |
|
171 | 176 | def column_value(column, issue, value) |
|
172 | 177 | case column.name |
|
173 | 178 | when :id |
|
174 | 179 | link_to value, issue_path(issue) |
|
175 | 180 | when :subject |
|
176 | 181 | link_to value, issue_path(issue) |
|
177 | 182 | when :parent |
|
178 | 183 | value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : '' |
|
179 | 184 | when :description |
|
180 | 185 | issue.description? ? content_tag('div', textilizable(issue, :description), :class => "wiki") : '' |
|
181 | 186 | when :done_ratio |
|
182 | 187 | progress_bar(value) |
|
183 | 188 | when :relations |
|
184 | 189 | content_tag('span', |
|
185 | 190 | value.to_s(issue) {|other| link_to_issue(other, :subject => false, :tracker => false)}.html_safe, |
|
186 | 191 | :class => value.css_classes_for(issue)) |
|
192 | when :hours, :spent_hours, :total_spent_hours, :estimated_hours | |
|
193 | format_hours(value) | |
|
187 | 194 | else |
|
188 | 195 | format_object(value) |
|
189 | 196 | end |
|
190 | 197 | end |
|
191 | 198 | |
|
192 | 199 | def csv_content(column, issue) |
|
193 | 200 | value = column.value_object(issue) |
|
194 | 201 | if value.is_a?(Array) |
|
195 | 202 | value.collect {|v| csv_value(column, issue, v)}.compact.join(', ') |
|
196 | 203 | else |
|
197 | 204 | csv_value(column, issue, value) |
|
198 | 205 | end |
|
199 | 206 | end |
|
200 | 207 | |
|
201 | 208 | def csv_value(column, object, value) |
|
202 | 209 | format_object(value, false) do |value| |
|
203 | 210 | case value.class.name |
|
204 | 211 | when 'Float' |
|
205 | 212 | sprintf("%.2f", value).gsub('.', l(:general_csv_decimal_separator)) |
|
206 | 213 | when 'IssueRelation' |
|
207 | 214 | value.to_s(object) |
|
208 | 215 | when 'Issue' |
|
209 | 216 | if object.is_a?(TimeEntry) |
|
210 | 217 | "#{value.tracker} ##{value.id}: #{value.subject}" |
|
211 | 218 | else |
|
212 | 219 | value.id |
|
213 | 220 | end |
|
214 | 221 | else |
|
215 | 222 | value |
|
216 | 223 | end |
|
217 | 224 | end |
|
218 | 225 | end |
|
219 | 226 | |
|
220 | 227 | def query_to_csv(items, query, options={}) |
|
221 | 228 | options ||= {} |
|
222 | 229 | columns = (options[:columns] == 'all' ? query.available_inline_columns : query.inline_columns) |
|
223 | 230 | query.available_block_columns.each do |column| |
|
224 | 231 | if options[column.name].present? |
|
225 | 232 | columns << column |
|
226 | 233 | end |
|
227 | 234 | end |
|
228 | 235 | |
|
229 | 236 | Redmine::Export::CSV.generate do |csv| |
|
230 | 237 | # csv header fields |
|
231 | 238 | csv << columns.map {|c| c.caption.to_s} |
|
232 | 239 | # csv lines |
|
233 | 240 | items.each do |item| |
|
234 | 241 | csv << columns.map {|c| csv_content(c, item)} |
|
235 | 242 | end |
|
236 | 243 | end |
|
237 | 244 | end |
|
238 | 245 | |
|
239 | 246 | # Retrieve query from session or build a new query |
|
240 | 247 | def retrieve_query(klass=IssueQuery, use_session=true) |
|
241 | 248 | session_key = klass.name.underscore.to_sym |
|
242 | 249 | |
|
243 | 250 | if params[:query_id].present? |
|
244 | 251 | cond = "project_id IS NULL" |
|
245 | 252 | cond << " OR project_id = #{@project.id}" if @project |
|
246 | 253 | @query = klass.where(cond).find(params[:query_id]) |
|
247 | 254 | raise ::Unauthorized unless @query.visible? |
|
248 | 255 | @query.project = @project |
|
249 | 256 | session[session_key] = {:id => @query.id, :project_id => @query.project_id} if use_session |
|
250 | 257 | sort_clear |
|
251 | 258 | elsif api_request? || params[:set_filter] || !use_session || session[session_key].nil? || session[session_key][:project_id] != (@project ? @project.id : nil) |
|
252 | 259 | # Give it a name, required to be valid |
|
253 | 260 | @query = klass.new(:name => "_", :project => @project) |
|
254 | 261 | @query.build_from_params(params) |
|
255 | 262 | session[session_key] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names, :totalable_names => @query.totalable_names} if use_session |
|
256 | 263 | else |
|
257 | 264 | # retrieve from session |
|
258 | 265 | @query = nil |
|
259 | 266 | @query = klass.find_by_id(session[session_key][:id]) if session[session_key][:id] |
|
260 | 267 | @query ||= klass.new(:name => "_", :filters => session[session_key][:filters], :group_by => session[session_key][:group_by], :column_names => session[session_key][:column_names], :totalable_names => session[session_key][:totalable_names]) |
|
261 | 268 | @query.project = @project |
|
262 | 269 | end |
|
263 | 270 | end |
|
264 | 271 | |
|
265 | 272 | def retrieve_query_from_session(klass=IssueQuery) |
|
266 | 273 | session_key = klass.name.underscore.to_sym |
|
267 | 274 | session_data = session[session_key] |
|
268 | 275 | |
|
269 | 276 | if session_data |
|
270 | 277 | if session_data[:id] |
|
271 | 278 | @query = IssueQuery.find_by_id(session_data[:id]) |
|
272 | 279 | return unless @query |
|
273 | 280 | else |
|
274 | 281 | @query = IssueQuery.new(:name => "_", :filters => session_data[:filters], :group_by => session_data[:group_by], :column_names => session_data[:column_names], :totalable_names => session_data[:totalable_names]) |
|
275 | 282 | end |
|
276 | 283 | if session_data.has_key?(:project_id) |
|
277 | 284 | @query.project_id = session_data[:project_id] |
|
278 | 285 | else |
|
279 | 286 | @query.project = @project |
|
280 | 287 | end |
|
281 | 288 | @query |
|
282 | 289 | end |
|
283 | 290 | end |
|
284 | 291 | |
|
285 | 292 | # Returns the query definition as hidden field tags |
|
286 | 293 | def query_as_hidden_field_tags(query) |
|
287 | 294 | tags = hidden_field_tag("set_filter", "1", :id => nil) |
|
288 | 295 | |
|
289 | 296 | if query.filters.present? |
|
290 | 297 | query.filters.each do |field, filter| |
|
291 | 298 | tags << hidden_field_tag("f[]", field, :id => nil) |
|
292 | 299 | tags << hidden_field_tag("op[#{field}]", filter[:operator], :id => nil) |
|
293 | 300 | filter[:values].each do |value| |
|
294 | 301 | tags << hidden_field_tag("v[#{field}][]", value, :id => nil) |
|
295 | 302 | end |
|
296 | 303 | end |
|
297 | 304 | else |
|
298 | 305 | tags << hidden_field_tag("f[]", "", :id => nil) |
|
299 | 306 | end |
|
300 | 307 | if query.column_names.present? |
|
301 | 308 | query.column_names.each do |name| |
|
302 | 309 | tags << hidden_field_tag("c[]", name, :id => nil) |
|
303 | 310 | end |
|
304 | 311 | end |
|
305 | 312 | if query.totalable_names.present? |
|
306 | 313 | query.totalable_names.each do |name| |
|
307 | 314 | tags << hidden_field_tag("t[]", name, :id => nil) |
|
308 | 315 | end |
|
309 | 316 | end |
|
310 | 317 | if query.group_by.present? |
|
311 | 318 | tags << hidden_field_tag("group_by", query.group_by, :id => nil) |
|
312 | 319 | end |
|
313 | 320 | |
|
314 | 321 | tags |
|
315 | 322 | end |
|
316 | 323 | |
|
317 | 324 | # Returns the queries that are rendered in the sidebar |
|
318 | 325 | def sidebar_queries(klass, project) |
|
319 | 326 | klass.visible.global_or_on_project(@project).sorted.to_a |
|
320 | 327 | end |
|
321 | 328 | |
|
322 | 329 | # Renders a group of queries |
|
323 | 330 | def query_links(title, queries) |
|
324 | 331 | return '' if queries.empty? |
|
325 | 332 | # links to #index on issues/show |
|
326 | 333 | url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : {} |
|
327 | 334 | |
|
328 | 335 | content_tag('h3', title) + "\n" + |
|
329 | 336 | content_tag('ul', |
|
330 | 337 | queries.collect {|query| |
|
331 | 338 | css = 'query' |
|
332 | 339 | css << ' selected' if query == @query |
|
333 | 340 | content_tag('li', link_to(query.name, url_params.merge(:query_id => query), :class => css)) |
|
334 | 341 | }.join("\n").html_safe, |
|
335 | 342 | :class => 'queries' |
|
336 | 343 | ) + "\n" |
|
337 | 344 | end |
|
338 | 345 | |
|
339 | 346 | # Renders the list of queries for the sidebar |
|
340 | 347 | def render_sidebar_queries(klass, project) |
|
341 | 348 | queries = sidebar_queries(klass, project) |
|
342 | 349 | |
|
343 | 350 | out = ''.html_safe |
|
344 | 351 | out << query_links(l(:label_my_queries), queries.select(&:is_private?)) |
|
345 | 352 | out << query_links(l(:label_query_plural), queries.reject(&:is_private?)) |
|
346 | 353 | out |
|
347 | 354 | end |
|
348 | 355 | end |
@@ -1,83 +1,83 | |||
|
1 | 1 | <%= labelled_fields_for :issue, @issue do |f| %> |
|
2 | 2 | |
|
3 | 3 | <div class="splitcontent"> |
|
4 | 4 | <div class="splitcontentleft"> |
|
5 | 5 | <% if @issue.safe_attribute?('status_id') && @allowed_statuses.present? %> |
|
6 | 6 | <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), {:required => true}, |
|
7 | 7 | :onchange => "updateIssueFrom('#{escape_javascript update_issue_form_path(@project, @issue)}', this)" %></p> |
|
8 | 8 | <%= hidden_field_tag 'was_default_status', @issue.status_id, :id => nil if @issue.status == @issue.default_status %> |
|
9 | 9 | <% else %> |
|
10 | 10 | <p><label><%= l(:field_status) %></label> <%= @issue.status %></p> |
|
11 | 11 | <% end %> |
|
12 | 12 | |
|
13 | 13 | <% if @issue.safe_attribute? 'priority_id' %> |
|
14 | 14 | <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), {:required => true} %></p> |
|
15 | 15 | <% end %> |
|
16 | 16 | |
|
17 | 17 | <% if @issue.safe_attribute? 'assigned_to_id' %> |
|
18 | 18 | <p><%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to), :include_blank => true, :required => @issue.required_attribute?('assigned_to_id') %></p> |
|
19 | 19 | <% end %> |
|
20 | 20 | |
|
21 | 21 | <% if @issue.safe_attribute?('category_id') && @issue.project.issue_categories.any? %> |
|
22 | 22 | <p><%= f.select :category_id, (@issue.project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true, :required => @issue.required_attribute?('category_id') %> |
|
23 | 23 | <%= link_to(l(:label_issue_category_new), |
|
24 | 24 | new_project_issue_category_path(@issue.project), |
|
25 | 25 | :remote => true, |
|
26 | 26 | :method => 'get', |
|
27 | 27 | :title => l(:label_issue_category_new), |
|
28 | 28 | :tabindex => 200, |
|
29 | 29 | :class => 'icon-only icon-add' |
|
30 | 30 | ) if User.current.allowed_to?(:manage_categories, @issue.project) %></p> |
|
31 | 31 | <% end %> |
|
32 | 32 | |
|
33 | 33 | <% if @issue.safe_attribute?('fixed_version_id') && @issue.assignable_versions.any? %> |
|
34 | 34 | <p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true, :required => @issue.required_attribute?('fixed_version_id') %> |
|
35 | 35 | <%= link_to(l(:label_version_new), |
|
36 | 36 | new_project_version_path(@issue.project), |
|
37 | 37 | :remote => true, |
|
38 | 38 | :method => 'get', |
|
39 | 39 | :title => l(:label_version_new), |
|
40 | 40 | :tabindex => 200, |
|
41 | 41 | :class => 'icon-only icon-add' |
|
42 | 42 | ) if User.current.allowed_to?(:manage_versions, @issue.project) %> |
|
43 | 43 | </p> |
|
44 | 44 | <% end %> |
|
45 | 45 | </div> |
|
46 | 46 | |
|
47 | 47 | <div class="splitcontentright"> |
|
48 | 48 | <% if @issue.safe_attribute? 'parent_issue_id' %> |
|
49 | 49 | <p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10, :required => @issue.required_attribute?('parent_issue_id') %></p> |
|
50 | 50 | <%= javascript_tag "observeAutocompleteField('issue_parent_issue_id', '#{escape_javascript auto_complete_issues_path(:project_id => @issue.project, :scope => Setting.cross_project_subtasks)}')" %> |
|
51 | 51 | <% end %> |
|
52 | 52 | |
|
53 | 53 | <% if @issue.safe_attribute? 'start_date' %> |
|
54 | 54 | <p id="start_date_area"> |
|
55 | 55 | <%= f.date_field(:start_date, :size => 10, :required => @issue.required_attribute?('start_date')) %> |
|
56 | 56 | <%= calendar_for('issue_start_date') %> |
|
57 | 57 | </p> |
|
58 | 58 | <% end %> |
|
59 | 59 | |
|
60 | 60 | <% if @issue.safe_attribute? 'due_date' %> |
|
61 | 61 | <p id="due_date_area"> |
|
62 | 62 | <%= f.date_field(:due_date, :size => 10, :required => @issue.required_attribute?('due_date')) %> |
|
63 | 63 | <%= calendar_for('issue_due_date') %> |
|
64 | 64 | </p> |
|
65 | 65 | <% end %> |
|
66 | 66 | |
|
67 | 67 | <% if @issue.safe_attribute? 'estimated_hours' %> |
|
68 | <p><%= f.text_field :estimated_hours, :size => 3, :required => @issue.required_attribute?('estimated_hours') %> <%= l(:field_hours) %></p> | |
|
68 | <p><%= f.text_field :estimated_hours, :size => 3, :required => @issue.required_attribute?('estimated_hours'), :value => format_hours(@issue.estimated_hours) %> <%= l(:field_hours) %></p> | |
|
69 | 69 | <% end %> |
|
70 | 70 | |
|
71 | 71 | <% if @issue.safe_attribute?('done_ratio') && Issue.use_field_for_done_ratio? %> |
|
72 | 72 | <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }), :required => @issue.required_attribute?('done_ratio') %></p> |
|
73 | 73 | <% end %> |
|
74 | 74 | </div> |
|
75 | 75 | </div> |
|
76 | 76 | |
|
77 | 77 | <% if @issue.safe_attribute? 'custom_field_values' %> |
|
78 | 78 | <%= render :partial => 'issues/form_custom_fields' %> |
|
79 | 79 | <% end %> |
|
80 | 80 | |
|
81 | 81 | <% end %> |
|
82 | 82 | |
|
83 | 83 | <% include_calendar_headers_tags %> |
@@ -1,80 +1,80 | |||
|
1 | 1 | <%= labelled_form_for @issue, :html => {:id => 'issue-form', :multipart => true} do |f| %> |
|
2 | 2 | <%= error_messages_for 'issue', 'time_entry' %> |
|
3 | 3 | <%= render :partial => 'conflict' if @conflict %> |
|
4 | 4 | <div class="box"> |
|
5 | 5 | <% if @issue.attributes_editable? %> |
|
6 | 6 | <fieldset class="tabular"><legend><%= l(:label_change_properties) %></legend> |
|
7 | 7 | <div id="all_attributes"> |
|
8 | 8 | <%= render :partial => 'form', :locals => {:f => f} %> |
|
9 | 9 | </div> |
|
10 | 10 | </fieldset> |
|
11 | 11 | <% end %> |
|
12 | 12 | <% if User.current.allowed_to?(:log_time, @project) %> |
|
13 | 13 | <fieldset class="tabular"><legend><%= l(:button_log_time) %></legend> |
|
14 | 14 | <%= labelled_fields_for :time_entry, @time_entry do |time_entry| %> |
|
15 | 15 | <div class="splitcontent"> |
|
16 | 16 | <div class="splitcontentleft"> |
|
17 | <p><%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %></p> | |
|
17 | <p><%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time, :value => format_hours(@time_entry.hours) %> <%= l(:field_hours) %></p> | |
|
18 | 18 | </div> |
|
19 | 19 | <div class="splitcontentright"> |
|
20 | 20 | <p><%= time_entry.select :activity_id, activity_collection_for_select_options %></p> |
|
21 | 21 | </div> |
|
22 | 22 | </div> |
|
23 | 23 | <p><%= time_entry.text_field :comments, :size => 60 %></p> |
|
24 | 24 | <% @time_entry.custom_field_values.each do |value| %> |
|
25 | 25 | <p><%= custom_field_tag_with_label :time_entry, value %></p> |
|
26 | 26 | <% end %> |
|
27 | 27 | <% end %> |
|
28 | 28 | </fieldset> |
|
29 | 29 | <% end %> |
|
30 | 30 | <% if @issue.notes_addable? %> |
|
31 | 31 | <fieldset><legend><%= l(:field_notes) %></legend> |
|
32 | 32 | <%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit', :no_label => true %> |
|
33 | 33 | <%= wikitoolbar_for 'issue_notes' %> |
|
34 | 34 | |
|
35 | 35 | <% if @issue.safe_attribute? 'private_notes' %> |
|
36 | 36 | <%= f.check_box :private_notes, :no_label => true %> <label for="issue_private_notes"><%= l(:field_private_notes) %></label> |
|
37 | 37 | <% end %> |
|
38 | 38 | |
|
39 | 39 | <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %> |
|
40 | 40 | </fieldset> |
|
41 | 41 | |
|
42 | 42 | <fieldset><legend><%= l(:label_attachment_plural) %></legend> |
|
43 | 43 | <% if @issue.attachments.any? && @issue.safe_attribute?('deleted_attachment_ids') %> |
|
44 | 44 | <div class="contextual"><%= link_to l(:label_edit_attachments), '#', :onclick => "$('#existing-attachments').toggle(); return false;" %></div> |
|
45 | 45 | <div id="existing-attachments" style="<%= @issue.deleted_attachment_ids.blank? ? 'display:none;' : '' %>"> |
|
46 | 46 | <% @issue.attachments.each do |attachment| %> |
|
47 | 47 | <span class="existing-attachment"> |
|
48 | 48 | <%= text_field_tag '', attachment.filename, :class => "filename", :disabled => true %> |
|
49 | 49 | <label> |
|
50 | 50 | <%= check_box_tag 'issue[deleted_attachment_ids][]', |
|
51 | 51 | attachment.id, |
|
52 | 52 | @issue.deleted_attachment_ids.include?(attachment.id), |
|
53 | 53 | :id => nil, :class => "deleted_attachment" %> <%= l(:button_delete) %> |
|
54 | 54 | </label> |
|
55 | 55 | </span> |
|
56 | 56 | <% end %> |
|
57 | 57 | <hr /> |
|
58 | 58 | </div> |
|
59 | 59 | <% end %> |
|
60 | 60 | |
|
61 | 61 | <div id="new-attachments" style="display:inline-block;"> |
|
62 | 62 | <%= render :partial => 'attachments/form', :locals => {:container => @issue} %> |
|
63 | 63 | </div> |
|
64 | 64 | </fieldset> |
|
65 | 65 | <% end %> |
|
66 | 66 | </div> |
|
67 | 67 | |
|
68 | 68 | <%= f.hidden_field :lock_version %> |
|
69 | 69 | <%= hidden_field_tag 'last_journal_id', params[:last_journal_id] || @issue.last_journal_id %> |
|
70 | 70 | <%= submit_tag l(:button_submit) %> |
|
71 | 71 | <%= preview_link preview_edit_issue_path(:project_id => @project, :id => @issue), 'issue-form' %> |
|
72 | 72 | | <%= link_to l(:button_cancel), {}, :onclick => "$('#update').hide(); return false;" %> |
|
73 | 73 | |
|
74 | 74 | <%= hidden_field_tag 'prev_issue_id', @prev_issue_id if @prev_issue_id %> |
|
75 | 75 | <%= hidden_field_tag 'next_issue_id', @next_issue_id if @next_issue_id %> |
|
76 | 76 | <%= hidden_field_tag 'issue_position', @issue_position if @issue_position %> |
|
77 | 77 | <%= hidden_field_tag 'issue_count', @issue_count if @issue_count %> |
|
78 | 78 | <% end %> |
|
79 | 79 | |
|
80 | 80 | <div id="preview" class="wiki"></div> |
@@ -1,68 +1,68 | |||
|
1 | 1 | <% |
|
2 | 2 | entries, days = timelog_items(settings) |
|
3 | 3 | entries_by_day = entries.group_by(&:spent_on) |
|
4 | 4 | %> |
|
5 | 5 | |
|
6 | 6 | <div class="contextual"> |
|
7 | 7 | <%= link_to l(:button_log_time), new_time_entry_path, :class => "icon icon-add" if User.current.allowed_to?(:log_time, nil, :global => true) %> |
|
8 | 8 | <%= link_to_function l(:label_options), "$('#timelog-settings').toggle();", :class => 'icon-only icon-settings' %> |
|
9 | 9 | </div> |
|
10 | 10 | |
|
11 | 11 | <h3> |
|
12 | 12 | <%= link_to l(:label_spent_time), time_entries_path(:user_id => 'me') %> |
|
13 | 13 | (<%= l(:label_last_n_days, days) %>: <%= l_hours_short entries.sum(&:hours) %>) |
|
14 | 14 | </h3> |
|
15 | 15 | |
|
16 | 16 | |
|
17 | 17 | <div id="timelog-settings" style="display:none;"> |
|
18 | 18 | <%= form_tag({}, :remote => true) do %> |
|
19 | 19 | <div class="box"> |
|
20 | 20 | <p> |
|
21 | 21 | <label> |
|
22 | 22 | <%= l(:button_show) %>: |
|
23 | 23 | <%= text_field_tag 'settings[timelog][days]', days, :size => 6 %> |
|
24 | 24 | <%= l(:label_day_plural) %> |
|
25 | 25 | </label> |
|
26 | 26 | </p> |
|
27 | 27 | </div> |
|
28 | 28 | <p> |
|
29 | 29 | <%= submit_tag l(:button_save) %> |
|
30 | 30 | <%= link_to_function l(:button_cancel), "$('#timelog-settings').toggle();" %> |
|
31 | 31 | </p> |
|
32 | 32 | <% end %> |
|
33 | 33 | </div> |
|
34 | 34 | |
|
35 | 35 | <% if entries.any? %> |
|
36 | 36 | <%= form_tag({}, :data => {:cm_url => time_entries_context_menu_path}) do %> |
|
37 | 37 | <table class="list time-entries"> |
|
38 | 38 | <thead><tr> |
|
39 | 39 | <th><%= l(:label_activity) %></th> |
|
40 | 40 | <th><%= l(:label_project) %></th> |
|
41 | 41 | <th><%= l(:field_comments) %></th> |
|
42 | 42 | <th><%= l(:field_hours) %></th> |
|
43 | 43 | </tr></thead> |
|
44 | 44 | <tbody> |
|
45 | 45 | <% entries_by_day.keys.sort.reverse.each do |day| %> |
|
46 | 46 | <tr class="odd"> |
|
47 | 47 | <td><strong><%= day == User.current.today ? l(:label_today).titleize : format_date(day) %></strong></td> |
|
48 | 48 | <td colspan="2"></td> |
|
49 |
<td class="hours"><em><%= html_hours( |
|
|
49 | <td class="hours"><em><%= html_hours(l_hours_short(entries_by_day[day].sum(&:hours))) %></em></td> | |
|
50 | 50 | </tr> |
|
51 | 51 | <% entries_by_day[day].each do |entry| -%> |
|
52 | 52 | <tr class="time-entry hascontextmenu"> |
|
53 | 53 | <td class="activity"> |
|
54 | 54 | <%= check_box_tag("ids[]", entry.id, false, :style => 'display:none;', :id => nil) %> |
|
55 | 55 | <%= entry.activity %> |
|
56 | 56 | </td> |
|
57 | 57 | <td class="subject"><%= entry.project %> <%= h(' - ') + link_to_issue(entry.issue, :truncate => 50) if entry.issue %></td> |
|
58 | 58 | <td class="comments"><%= entry.comments %></td> |
|
59 |
<td class="hours"><%= html_hours( |
|
|
59 | <td class="hours"><%= html_hours(l_hours_short(entry.hours)) %></td> | |
|
60 | 60 | </tr> |
|
61 | 61 | <% end -%> |
|
62 | 62 | <% end -%> |
|
63 | 63 | </tbody> |
|
64 | 64 | </table> |
|
65 | 65 | <% end %> |
|
66 | 66 | <% else %> |
|
67 | 67 | <p class="nodata"><%= l(:label_no_data) %></p> |
|
68 | 68 | <% end %> |
@@ -1,32 +1,34 | |||
|
1 | 1 | <%= form_tag({:action => 'edit', :tab => 'display'}) do %> |
|
2 | 2 | |
|
3 | 3 | <div class="box tabular settings"> |
|
4 | 4 | <p><%= setting_select :ui_theme, Redmine::Themes.themes.collect {|t| [t.name, t.id]}, :blank => :label_default, :label => :label_theme %></p> |
|
5 | 5 | |
|
6 | 6 | <p><%= setting_select :default_language, lang_options_for_select(false) %></p> |
|
7 | 7 | |
|
8 | 8 | <p><%= setting_check_box :force_default_language_for_anonymous %></p> |
|
9 | 9 | |
|
10 | 10 | <p><%= setting_check_box :force_default_language_for_loggedin %></p> |
|
11 | 11 | |
|
12 | 12 | <p><%= setting_select :start_of_week, [[day_name(1),'1'], [day_name(6),'6'], [day_name(7),'7']], :blank => :label_language_based %></p> |
|
13 | 13 | <% locale = User.current.language.blank? ? ::I18n.locale : User.current.language %> |
|
14 | 14 | <p><%= setting_select :date_format, date_format_setting_options(locale), :blank => :label_language_based %></p> |
|
15 | 15 | |
|
16 | 16 | <p><%= setting_select :time_format, Setting::TIME_FORMATS.collect {|f| [::I18n.l(Time.now, :locale => locale, :format => f), f]}, :blank => :label_language_based %></p> |
|
17 | 17 | |
|
18 | <p><%= setting_select :timespan_format, [["%.2f" % 0.75, 'decimal'], ['0:45 h', 'minutes']], :blank => false %></p> | |
|
19 | ||
|
18 | 20 | <p><%= setting_select :user_format, @options[:user_format] %></p> |
|
19 | 21 | |
|
20 | 22 | <p><%= setting_check_box :gravatar_enabled %></p> |
|
21 | 23 | |
|
22 | 24 | <p><%= setting_select :gravatar_default, [["Wavatars", 'wavatar'], ["Identicons", 'identicon'], ["Monster ids", 'monsterid'], ["Retro", 'retro'], ["Mystery man", 'mm']], :blank => :label_none %></p> |
|
23 | 25 | |
|
24 | 26 | <p><%= setting_check_box :thumbnails_enabled %></p> |
|
25 | 27 | |
|
26 | 28 | <p><%= setting_text_field :thumbnails_size, :size => 6 %></p> |
|
27 | 29 | |
|
28 | 30 | <p><%= setting_select :new_item_menu_tab, [[l(:label_none), '0'], [l(:label_new_project_issue_tab_enabled), '1'], [l(:label_new_object_tab_enabled), '2']] %></p> |
|
29 | 31 | </div> |
|
30 | 32 | |
|
31 | 33 | <%= submit_tag l(:button_save) %> |
|
32 | 34 | <% end %> |
@@ -1,47 +1,47 | |||
|
1 | 1 | <%= error_messages_for 'time_entry' %> |
|
2 | 2 | <%= back_url_hidden_field_tag %> |
|
3 | 3 | |
|
4 | 4 | <div class="box tabular"> |
|
5 | 5 | <% if @time_entry.new_record? %> |
|
6 | 6 | <% if params[:project_id] %> |
|
7 | 7 | <%= hidden_field_tag 'project_id', params[:project_id] %> |
|
8 | 8 | <% elsif params[:issue_id] %> |
|
9 | 9 | <%= hidden_field_tag 'issue_id', params[:issue_id] %> |
|
10 | 10 | <% else %> |
|
11 | 11 | <p><%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).to_a, :selected => @time_entry.project, :include_blank => true), :required => true %></p> |
|
12 | 12 | <% end %> |
|
13 | 13 | <% end %> |
|
14 | 14 | <p> |
|
15 | 15 | <%= f.text_field :issue_id, :size => 6 %> |
|
16 | 16 | <span id="time_entry_issue"> |
|
17 | 17 | <%= link_to_issue(@time_entry.issue) if @time_entry.issue.try(:visible?) %> |
|
18 | 18 | </span> |
|
19 | 19 | </p> |
|
20 | 20 | <p><%= f.date_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p> |
|
21 | <p><%= f.text_field :hours, :size => 6, :required => true %></p> | |
|
21 | <p><%= f.text_field :hours, :size => 6, :required => true, :value => format_hours(@time_entry.hours) %></p> | |
|
22 | 22 | <p><%= f.text_field :comments, :size => 100, :maxlength => 1024 %></p> |
|
23 | 23 | <p><%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %></p> |
|
24 | 24 | <% @time_entry.custom_field_values.each do |value| %> |
|
25 | 25 | <p><%= custom_field_tag_with_label :time_entry, value %></p> |
|
26 | 26 | <% end %> |
|
27 | 27 | <%= call_hook(:view_timelog_edit_form_bottom, { :time_entry => @time_entry, :form => f }) %> |
|
28 | 28 | </div> |
|
29 | 29 | |
|
30 | 30 | <%= javascript_tag do %> |
|
31 | 31 | $(document).ready(function(){ |
|
32 | 32 | $('#time_entry_project_id, #time_entry_issue_id').change(function(){ |
|
33 | 33 | $.ajax({ |
|
34 | 34 | url: '<%= escape_javascript(@time_entry.new_record? ? new_time_entry_path(:format => 'js') : edit_time_entry_path(:format => 'js')) %>', |
|
35 | 35 | type: 'post', |
|
36 | 36 | data: $(this).closest('form').serialize() |
|
37 | 37 | }); |
|
38 | 38 | }); |
|
39 | 39 | }); |
|
40 | 40 | |
|
41 | 41 | observeAutocompleteField('time_entry_issue_id', '<%= escape_javascript auto_complete_issues_path(:project_id => @project, :scope => (@project ? nil : 'all'))%>', { |
|
42 | 42 | select: function(event, ui) { |
|
43 | 43 | $('#time_entry_issue').text(''); |
|
44 | 44 | $('#time_entry_issue_id').val(ui.item.value).change(); |
|
45 | 45 | } |
|
46 | 46 | }); |
|
47 | 47 | <% end %> |
@@ -1,19 +1,19 | |||
|
1 | 1 | <% @report.hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value| %> |
|
2 | 2 | <% hours_for_value = select_hours(hours, criterias[level], value) -%> |
|
3 | 3 | <% next if hours_for_value.empty? -%> |
|
4 | 4 | <tr class="<%= cycle('odd', 'even') %> <%= criterias.length > level+1 ? 'subtotal' : 'last-level' %>"> |
|
5 | 5 | <%= ("<td></td>" * level).html_safe %> |
|
6 | 6 | <td class="name"><%= format_criteria_value(@report.available_criteria[criterias[level]], value) %></td> |
|
7 | 7 | <%= ("<td></td>" * (criterias.length - level - 1)).html_safe -%> |
|
8 | 8 | <% total = 0 -%> |
|
9 | 9 | <% @report.periods.each do |period| -%> |
|
10 | 10 | <% sum = sum_hours(select_hours(hours_for_value, @report.columns, period.to_s)); total += sum -%> |
|
11 |
<td class="hours"><%= html_hours( |
|
|
11 | <td class="hours"><%= html_hours(l_hours_short(sum)) if sum > 0 %></td> | |
|
12 | 12 | <% end -%> |
|
13 |
<td class="hours"><%= html_hours( |
|
|
13 | <td class="hours"><%= html_hours(l_hours_short(total)) if total > 0 %></td> | |
|
14 | 14 | </tr> |
|
15 | 15 | <% if criterias.length > level+1 -%> |
|
16 | 16 | <%= render(:partial => 'report_criteria', :locals => {:criterias => criterias, :hours => hours_for_value, :level => (level + 1)}) %> |
|
17 | 17 | <% end -%> |
|
18 | 18 | |
|
19 | 19 | <% end %> |
@@ -1,74 +1,74 | |||
|
1 | 1 | <div class="contextual"> |
|
2 | 2 | <%= link_to l(:button_log_time), |
|
3 | 3 | _new_time_entry_path(@project, @issue), |
|
4 | 4 | :class => 'icon icon-time-add' if User.current.allowed_to?(:log_time, @project, :global => true) %> |
|
5 | 5 | </div> |
|
6 | 6 | |
|
7 | 7 | <h2><%= @query.new_record? ? l(:label_spent_time) : @query.name %></h2> |
|
8 | 8 | |
|
9 | 9 | <%= form_tag(_report_time_entries_path(@project, nil), :method => :get, :id => 'query_form') do %> |
|
10 | 10 | <% @report.criteria.each do |criterion| %> |
|
11 | 11 | <%= hidden_field_tag 'criteria[]', criterion, :id => nil %> |
|
12 | 12 | <% end %> |
|
13 | 13 | <%= render :partial => 'timelog/date_range' %> |
|
14 | 14 | |
|
15 | 15 | <p><label for='columns'><%= l(:label_details) %></label>: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'], |
|
16 | 16 | [l(:label_month), 'month'], |
|
17 | 17 | [l(:label_week), 'week'], |
|
18 | 18 | [l(:label_day_plural).titleize, 'day']], @report.columns), |
|
19 | 19 | :onchange => "this.form.submit();" %> |
|
20 | 20 | |
|
21 | 21 | <label for='criterias'><%= l(:button_add) %></label>: <%= select_tag('criteria[]', options_for_select([[]] + (@report.available_criteria.keys - @report.criteria).collect{|k| [l_or_humanize(@report.available_criteria[k][:label]), k]}), |
|
22 | 22 | :onchange => "this.form.submit();", |
|
23 | 23 | :style => 'width: 200px', |
|
24 | 24 | :disabled => (@report.criteria.length >= 3), |
|
25 | 25 | :id => "criterias") %> |
|
26 | 26 | <%= link_to l(:button_clear), {:project_id => @project, :issue_id => @issue, :period_type => params[:period_type], :period => params[:period], :from => @from, :to => @to, :columns => @report.columns}, :class => 'icon icon-reload' %></p> |
|
27 | 27 | <% end %> |
|
28 | 28 | |
|
29 | 29 | <% unless @report.criteria.empty? %> |
|
30 | 30 | <% if @report.hours.empty? %> |
|
31 | 31 | <p class="nodata"><%= l(:label_no_data) %></p> |
|
32 | 32 | <% else %> |
|
33 | 33 | <div class="autoscroll"> |
|
34 | 34 | <table class="list" id="time-report"> |
|
35 | 35 | <thead> |
|
36 | 36 | <tr> |
|
37 | 37 | <% @report.criteria.each do |criteria| %> |
|
38 | 38 | <th><%= l_or_humanize(@report.available_criteria[criteria][:label]) %></th> |
|
39 | 39 | <% end %> |
|
40 | 40 | <% columns_width = (40 / (@report.periods.length+1)).to_i %> |
|
41 | 41 | <% @report.periods.each do |period| %> |
|
42 | 42 | <th class="period" style="width:<%= columns_width %>%;"><%= period %></th> |
|
43 | 43 | <% end %> |
|
44 | 44 | <th class="total" style="width:<%= columns_width %>%;"><%= l(:label_total_time) %></th> |
|
45 | 45 | </tr> |
|
46 | 46 | </thead> |
|
47 | 47 | <tbody> |
|
48 | 48 | <%= render :partial => 'report_criteria', :locals => {:criterias => @report.criteria, :hours => @report.hours, :level => 0} %> |
|
49 | 49 | <tr class="total"> |
|
50 | 50 | <td><%= l(:label_total_time) %></td> |
|
51 | 51 | <%= ('<td></td>' * (@report.criteria.size - 1)).html_safe %> |
|
52 | 52 | <% total = 0 -%> |
|
53 | 53 | <% @report.periods.each do |period| -%> |
|
54 | 54 | <% sum = sum_hours(select_hours(@report.hours, @report.columns, period.to_s)); total += sum -%> |
|
55 |
<td class="hours"><%= html_hours( |
|
|
55 | <td class="hours"><%= html_hours(l_hours_short(sum)) if sum > 0 %></td> | |
|
56 | 56 | <% end -%> |
|
57 |
<td class="hours"><%= html_hours( |
|
|
57 | <td class="hours"><%= html_hours(l_hours_short(total)) if total > 0 %></td> | |
|
58 | 58 | </tr> |
|
59 | 59 | </tbody> |
|
60 | 60 | </table> |
|
61 | 61 | </div> |
|
62 | 62 | |
|
63 | 63 | <% other_formats_links do |f| %> |
|
64 | 64 | <%= f.link_to 'CSV', :url => params %> |
|
65 | 65 | <% end %> |
|
66 | 66 | <% end %> |
|
67 | 67 | <% end %> |
|
68 | 68 | |
|
69 | 69 | <% content_for :sidebar do %> |
|
70 | 70 | <%= render_sidebar_queries(TimeEntryQuery, @project) %> |
|
71 | 71 | <% end %> |
|
72 | 72 | |
|
73 | 73 | <% html_title(@query.new_record? ? l(:label_spent_time) : @query.name, l(:label_report)) %> |
|
74 | 74 |
@@ -1,1217 +1,1218 | |||
|
1 | 1 | # German translations for Ruby on Rails |
|
2 | 2 | # by Clemens Kofler (clemens@railway.at) |
|
3 | 3 | # additions for Redmine 1.2 by Jens Martsch (jmartsch@gmail.com) |
|
4 | 4 | |
|
5 | 5 | de: |
|
6 | 6 | direction: ltr |
|
7 | 7 | date: |
|
8 | 8 | formats: |
|
9 | 9 | # Use the strftime parameters for formats. |
|
10 | 10 | # When no format has been given, it uses default. |
|
11 | 11 | # You can provide other formats here if you like! |
|
12 | 12 | default: "%d.%m.%Y" |
|
13 | 13 | short: "%e. %b" |
|
14 | 14 | long: "%e. %B %Y" |
|
15 | 15 | |
|
16 | 16 | day_names: [Sonntag, Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag] |
|
17 | 17 | abbr_day_names: [So, Mo, Di, Mi, Do, Fr, Sa] |
|
18 | 18 | |
|
19 | 19 | # Don't forget the nil at the beginning; there's no such thing as a 0th month |
|
20 | 20 | month_names: [~, Januar, Februar, MΓ€rz, April, Mai, Juni, Juli, August, September, Oktober, November, Dezember] |
|
21 | 21 | abbr_month_names: [~, Jan, Feb, MΓ€r, Apr, Mai, Jun, Jul, Aug, Sep, Okt, Nov, Dez] |
|
22 | 22 | # Used in date_select and datime_select. |
|
23 | 23 | order: |
|
24 | 24 | - :day |
|
25 | 25 | - :month |
|
26 | 26 | - :year |
|
27 | 27 | |
|
28 | 28 | time: |
|
29 | 29 | formats: |
|
30 | 30 | default: "%d.%m.%Y %H:%M" |
|
31 | 31 | time: "%H:%M" |
|
32 | 32 | short: "%e. %b %H:%M" |
|
33 | 33 | long: "%A, %e. %B %Y, %H:%M Uhr" |
|
34 | 34 | am: "vormittags" |
|
35 | 35 | pm: "nachmittags" |
|
36 | 36 | |
|
37 | 37 | datetime: |
|
38 | 38 | distance_in_words: |
|
39 | 39 | half_a_minute: 'eine halbe Minute' |
|
40 | 40 | less_than_x_seconds: |
|
41 | 41 | one: 'weniger als 1 Sekunde' |
|
42 | 42 | other: 'weniger als %{count} Sekunden' |
|
43 | 43 | x_seconds: |
|
44 | 44 | one: '1 Sekunde' |
|
45 | 45 | other: '%{count} Sekunden' |
|
46 | 46 | less_than_x_minutes: |
|
47 | 47 | one: 'weniger als 1 Minute' |
|
48 | 48 | other: 'weniger als %{count} Minuten' |
|
49 | 49 | x_minutes: |
|
50 | 50 | one: '1 Minute' |
|
51 | 51 | other: '%{count} Minuten' |
|
52 | 52 | about_x_hours: |
|
53 | 53 | one: 'etwa 1 Stunde' |
|
54 | 54 | other: 'etwa %{count} Stunden' |
|
55 | 55 | x_hours: |
|
56 | 56 | one: "1 Stunde" |
|
57 | 57 | other: "%{count} Stunden" |
|
58 | 58 | x_days: |
|
59 | 59 | one: '1 Tag' |
|
60 | 60 | other: '%{count} Tagen' |
|
61 | 61 | about_x_months: |
|
62 | 62 | one: 'etwa 1 Monat' |
|
63 | 63 | other: 'etwa %{count} Monaten' |
|
64 | 64 | x_months: |
|
65 | 65 | one: '1 Monat' |
|
66 | 66 | other: '%{count} Monaten' |
|
67 | 67 | about_x_years: |
|
68 | 68 | one: 'etwa 1 Jahr' |
|
69 | 69 | other: 'etwa %{count} Jahren' |
|
70 | 70 | over_x_years: |
|
71 | 71 | one: 'mehr als 1 Jahr' |
|
72 | 72 | other: 'mehr als %{count} Jahren' |
|
73 | 73 | almost_x_years: |
|
74 | 74 | one: "fast 1 Jahr" |
|
75 | 75 | other: "fast %{count} Jahren" |
|
76 | 76 | |
|
77 | 77 | number: |
|
78 | 78 | # Default format for numbers |
|
79 | 79 | format: |
|
80 | 80 | separator: ',' |
|
81 | 81 | delimiter: '.' |
|
82 | 82 | precision: 2 |
|
83 | 83 | currency: |
|
84 | 84 | format: |
|
85 | 85 | unit: 'β¬' |
|
86 | 86 | format: '%n %u' |
|
87 | 87 | delimiter: '' |
|
88 | 88 | percentage: |
|
89 | 89 | format: |
|
90 | 90 | delimiter: "" |
|
91 | 91 | precision: |
|
92 | 92 | format: |
|
93 | 93 | delimiter: "" |
|
94 | 94 | human: |
|
95 | 95 | format: |
|
96 | 96 | delimiter: "" |
|
97 | 97 | precision: 3 |
|
98 | 98 | storage_units: |
|
99 | 99 | format: "%n %u" |
|
100 | 100 | units: |
|
101 | 101 | byte: |
|
102 | 102 | one: "Byte" |
|
103 | 103 | other: "Bytes" |
|
104 | 104 | kb: "KB" |
|
105 | 105 | mb: "MB" |
|
106 | 106 | gb: "GB" |
|
107 | 107 | tb: "TB" |
|
108 | 108 | |
|
109 | 109 | # Used in array.to_sentence. |
|
110 | 110 | support: |
|
111 | 111 | array: |
|
112 | 112 | sentence_connector: "und" |
|
113 | 113 | skip_last_comma: true |
|
114 | 114 | |
|
115 | 115 | activerecord: |
|
116 | 116 | errors: |
|
117 | 117 | template: |
|
118 | 118 | header: |
|
119 | 119 | one: "Dieses %{model}-Objekt konnte nicht gespeichert werden: %{count} Fehler." |
|
120 | 120 | other: "Dieses %{model}-Objekt konnte nicht gespeichert werden: %{count} Fehler." |
|
121 | 121 | body: "Bitte ΓΌberprΓΌfen Sie die folgenden Felder:" |
|
122 | 122 | |
|
123 | 123 | messages: |
|
124 | 124 | inclusion: "ist kein gΓΌltiger Wert" |
|
125 | 125 | exclusion: "ist nicht verfΓΌgbar" |
|
126 | 126 | invalid: "ist nicht gΓΌltig" |
|
127 | 127 | confirmation: "stimmt nicht mit der BestΓ€tigung ΓΌberein" |
|
128 | 128 | accepted: "muss akzeptiert werden" |
|
129 | 129 | empty: "muss ausgefΓΌllt werden" |
|
130 | 130 | blank: "muss ausgefΓΌllt werden" |
|
131 | 131 | too_long: "ist zu lang (nicht mehr als %{count} Zeichen)" |
|
132 | 132 | too_short: "ist zu kurz (nicht weniger als %{count} Zeichen)" |
|
133 | 133 | wrong_length: "hat die falsche LΓ€nge (muss genau %{count} Zeichen haben)" |
|
134 | 134 | taken: "ist bereits vergeben" |
|
135 | 135 | not_a_number: "ist keine Zahl" |
|
136 | 136 | not_a_date: "ist kein gΓΌltiges Datum" |
|
137 | 137 | greater_than: "muss grΓΆΓer als %{count} sein" |
|
138 | 138 | greater_than_or_equal_to: "muss grΓΆΓer oder gleich %{count} sein" |
|
139 | 139 | equal_to: "muss genau %{count} sein" |
|
140 | 140 | less_than: "muss kleiner als %{count} sein" |
|
141 | 141 | less_than_or_equal_to: "muss kleiner oder gleich %{count} sein" |
|
142 | 142 | odd: "muss ungerade sein" |
|
143 | 143 | even: "muss gerade sein" |
|
144 | 144 | greater_than_start_date: "muss grΓΆΓer als Anfangsdatum sein" |
|
145 | 145 | not_same_project: "gehΓΆrt nicht zum selben Projekt" |
|
146 | 146 | circular_dependency: "Diese Beziehung wΓΌrde eine zyklische AbhΓ€ngigkeit erzeugen" |
|
147 | 147 | cant_link_an_issue_with_a_descendant: "Ein Ticket kann nicht mit einer Ihrer Unteraufgaben verlinkt werden" |
|
148 | 148 | earlier_than_minimum_start_date: "kann wegen eines VorgΓ€ngertickets nicht vor %{date} liegen" |
|
149 | 149 | |
|
150 | 150 | actionview_instancetag_blank_option: Bitte auswΓ€hlen |
|
151 | 151 | |
|
152 | 152 | button_activate: Aktivieren |
|
153 | 153 | button_add: HinzufΓΌgen |
|
154 | 154 | button_annotate: Annotieren |
|
155 | 155 | button_apply: Anwenden |
|
156 | 156 | button_archive: Archivieren |
|
157 | 157 | button_back: ZurΓΌck |
|
158 | 158 | button_cancel: Abbrechen |
|
159 | 159 | button_change: Wechseln |
|
160 | 160 | button_change_password: Passwort Γ€ndern |
|
161 | 161 | button_check_all: Alles auswΓ€hlen |
|
162 | 162 | button_clear: ZurΓΌcksetzen |
|
163 | 163 | button_close: SchlieΓen |
|
164 | 164 | button_collapse_all: Alle einklappen |
|
165 | 165 | button_configure: Konfigurieren |
|
166 | 166 | button_copy: Kopieren |
|
167 | 167 | button_copy_and_follow: Kopieren und Ticket anzeigen |
|
168 | 168 | button_create: Anlegen |
|
169 | 169 | button_create_and_continue: Anlegen und weiter |
|
170 | 170 | button_delete: LΓΆschen |
|
171 | 171 | button_delete_my_account: Mein Benutzerkonto lΓΆschen |
|
172 | 172 | button_download: Download |
|
173 | 173 | button_duplicate: Duplizieren |
|
174 | 174 | button_edit: Bearbeiten |
|
175 | 175 | button_edit_associated_wikipage: "ZugehΓΆrige Wikiseite bearbeiten: %{page_title}" |
|
176 | 176 | button_edit_section: Diesen Bereich bearbeiten |
|
177 | 177 | button_expand_all: Alle ausklappen |
|
178 | 178 | button_export: Exportieren |
|
179 | 179 | button_hide: Verstecken |
|
180 | 180 | button_list: Liste |
|
181 | 181 | button_lock: Sperren |
|
182 | 182 | button_log_time: Aufwand buchen |
|
183 | 183 | button_login: Anmelden |
|
184 | 184 | button_move: Verschieben |
|
185 | 185 | button_move_and_follow: Verschieben und Ticket anzeigen |
|
186 | 186 | button_quote: Zitieren |
|
187 | 187 | button_rename: Umbenennen |
|
188 | 188 | button_reopen: Γffnen |
|
189 | 189 | button_reply: Antworten |
|
190 | 190 | button_reset: ZurΓΌcksetzen |
|
191 | 191 | button_rollback: Auf diese Version zurΓΌcksetzen |
|
192 | 192 | button_save: Speichern |
|
193 | 193 | button_show: Anzeigen |
|
194 | 194 | button_sort: Sortieren |
|
195 | 195 | button_submit: OK |
|
196 | 196 | button_test: Testen |
|
197 | 197 | button_unarchive: Entarchivieren |
|
198 | 198 | button_uncheck_all: Alles abwΓ€hlen |
|
199 | 199 | button_unlock: Entsperren |
|
200 | 200 | button_unwatch: Nicht beobachten |
|
201 | 201 | button_update: Aktualisieren |
|
202 | 202 | button_view: Anzeigen |
|
203 | 203 | button_watch: Beobachten |
|
204 | 204 | |
|
205 | 205 | default_activity_design: Design |
|
206 | 206 | default_activity_development: Entwicklung |
|
207 | 207 | default_doc_category_tech: Technische Dokumentation |
|
208 | 208 | default_doc_category_user: Benutzerdokumentation |
|
209 | 209 | default_issue_status_closed: Erledigt |
|
210 | 210 | default_issue_status_feedback: Feedback |
|
211 | 211 | default_issue_status_in_progress: In Bearbeitung |
|
212 | 212 | default_issue_status_new: Neu |
|
213 | 213 | default_issue_status_rejected: Abgewiesen |
|
214 | 214 | default_issue_status_resolved: GelΓΆst |
|
215 | 215 | default_priority_high: Hoch |
|
216 | 216 | default_priority_immediate: Sofort |
|
217 | 217 | default_priority_low: Niedrig |
|
218 | 218 | default_priority_normal: Normal |
|
219 | 219 | default_priority_urgent: Dringend |
|
220 | 220 | default_role_developer: Entwickler |
|
221 | 221 | default_role_manager: Manager |
|
222 | 222 | default_role_reporter: Reporter |
|
223 | 223 | default_tracker_bug: Fehler |
|
224 | 224 | default_tracker_feature: Feature |
|
225 | 225 | default_tracker_support: UnterstΓΌtzung |
|
226 | 226 | |
|
227 | 227 | description_all_columns: Alle Spalten |
|
228 | 228 | description_available_columns: VerfΓΌgbare Spalten |
|
229 | 229 | description_choose_project: Projekte |
|
230 | 230 | description_date_from: Startdatum eintragen |
|
231 | 231 | description_date_range_interval: Zeitraum durch Start- und Enddatum festlegen |
|
232 | 232 | description_date_range_list: Zeitraum aus einer Liste wΓ€hlen |
|
233 | 233 | description_date_to: Enddatum eintragen |
|
234 | 234 | description_filter: Filter |
|
235 | 235 | description_issue_category_reassign: Neue Kategorie wΓ€hlen |
|
236 | 236 | description_message_content: Nachrichteninhalt |
|
237 | 237 | description_notes: Kommentare |
|
238 | 238 | description_project_scope: Suchbereich |
|
239 | 239 | description_query_sort_criteria_attribute: Sortierattribut |
|
240 | 240 | description_query_sort_criteria_direction: Sortierrichtung |
|
241 | 241 | description_search: Suchfeld |
|
242 | 242 | description_selected_columns: AusgewΓ€hlte Spalten |
|
243 | 243 | |
|
244 | 244 | description_user_mail_notification: Mailbenachrichtigungseinstellung |
|
245 | 245 | description_wiki_subpages_reassign: Neue Elternseite wΓ€hlen |
|
246 | 246 | |
|
247 | 247 | enumeration_activities: AktivitΓ€ten (Zeiterfassung) |
|
248 | 248 | enumeration_doc_categories: Dokumentenkategorien |
|
249 | 249 | enumeration_issue_priorities: Ticket-PrioritΓ€ten |
|
250 | 250 | enumeration_system_activity: System-AktivitΓ€t |
|
251 | 251 | |
|
252 | 252 | error_attachment_too_big: Diese Datei kann nicht hochgeladen werden, da sie die maximale DateigrΓΆΓe von (%{max_size}) ΓΌberschreitet. |
|
253 | 253 | error_can_not_archive_project: Dieses Projekt kann nicht archiviert werden. |
|
254 | 254 | error_can_not_delete_custom_field: Kann das benutzerdefinierte Feld nicht lΓΆschen. |
|
255 | 255 | error_can_not_delete_tracker: Dieser Tracker enthΓ€lt Tickets und kann nicht gelΓΆscht werden. |
|
256 | 256 | error_can_not_remove_role: Diese Rolle wird verwendet und kann nicht gelΓΆscht werden. |
|
257 | 257 | error_can_not_reopen_issue_on_closed_version: Das Ticket ist einer abgeschlossenen Version zugeordnet und kann daher nicht wieder geΓΆffnet werden. |
|
258 | 258 | error_can_t_load_default_data: "Die Standard-Konfiguration konnte nicht geladen werden: %{value}" |
|
259 | 259 | error_issue_done_ratios_not_updated: Der Ticket-Fortschritt wurde nicht aktualisiert. |
|
260 | 260 | error_issue_not_found_in_project: 'Das Ticket wurde nicht gefunden oder gehΓΆrt nicht zu diesem Projekt.' |
|
261 | 261 | error_no_default_issue_status: Es ist kein Status als Standard definiert. Bitte ΓΌberprΓΌfen Sie Ihre Konfiguration (unter "Administration -> Ticket-Status"). |
|
262 | 262 | error_no_tracker_in_project: Diesem Projekt ist kein Tracker zugeordnet. Bitte ΓΌberprΓΌfen Sie die Projekteinstellungen. |
|
263 | 263 | error_scm_annotate: "Der Eintrag existiert nicht oder kann nicht annotiert werden." |
|
264 | 264 | error_scm_annotate_big_text_file: Der Eintrag kann nicht umgesetzt werden, da er die maximale TextlΓ€nge ΓΌberschreitet. |
|
265 | 265 | error_scm_command_failed: "Beim Zugriff auf das Projektarchiv ist ein Fehler aufgetreten: %{value}" |
|
266 | 266 | error_scm_not_found: Eintrag und/oder Revision existiert nicht im Projektarchiv. |
|
267 | 267 | error_session_expired: Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an. |
|
268 | 268 | error_unable_delete_issue_status: "Der Ticket-Status konnte nicht gelΓΆscht werden." |
|
269 | 269 | error_unable_to_connect: Fehler beim Verbinden (%{value}) |
|
270 | 270 | error_workflow_copy_source: Bitte wΓ€hlen Sie einen Quell-Tracker und eine Quell-Rolle. |
|
271 | 271 | error_workflow_copy_target: Bitte wΓ€hlen Sie die Ziel-Tracker und -Rollen. |
|
272 | 272 | |
|
273 | 273 | field_account: Konto |
|
274 | 274 | field_active: Aktiv |
|
275 | 275 | field_activity: AktivitΓ€t |
|
276 | 276 | field_admin: Administrator |
|
277 | 277 | field_assignable: Tickets kΓΆnnen dieser Rolle zugewiesen werden |
|
278 | 278 | field_assigned_to: Zugewiesen an |
|
279 | 279 | field_assigned_to_role: ZustΓ€ndigkeitsrolle |
|
280 | 280 | field_attr_firstname: Vorname-Attribut |
|
281 | 281 | field_attr_lastname: Name-Attribut |
|
282 | 282 | field_attr_login: Mitgliedsname-Attribut |
|
283 | 283 | field_attr_mail: E-Mail-Attribut |
|
284 | 284 | field_auth_source: Authentifizierungs-Modus |
|
285 | 285 | field_auth_source_ldap_filter: LDAP-Filter |
|
286 | 286 | field_author: Autor |
|
287 | 287 | field_base_dn: Base DN |
|
288 | 288 | field_board_parent: Γbergeordnetes Forum |
|
289 | 289 | field_category: Kategorie |
|
290 | 290 | field_column_names: Spalten |
|
291 | 291 | field_closed_on: Geschlossen am |
|
292 | 292 | field_comments: Kommentar |
|
293 | 293 | field_comments_sorting: Kommentare anzeigen |
|
294 | 294 | field_commit_logs_encoding: Kodierung der Commit-Nachrichten |
|
295 | 295 | field_content: Inhalt |
|
296 | 296 | field_core_fields: Standardwerte |
|
297 | 297 | field_created_on: Angelegt |
|
298 | 298 | field_cvs_module: Modul |
|
299 | 299 | field_cvsroot: CVSROOT |
|
300 | 300 | field_default_value: Standardwert |
|
301 | 301 | field_default_status: Standardstatus |
|
302 | 302 | field_delay: Pufferzeit |
|
303 | 303 | field_description: Beschreibung |
|
304 | 304 | field_done_ratio: "% erledigt" |
|
305 | 305 | field_downloads: Downloads |
|
306 | 306 | field_due_date: Abgabedatum |
|
307 | 307 | field_editable: Bearbeitbar |
|
308 | 308 | field_effective_date: Datum |
|
309 | 309 | field_estimated_hours: GeschΓ€tzter Aufwand |
|
310 | 310 | field_field_format: Format |
|
311 | 311 | field_filename: Datei |
|
312 | 312 | field_filesize: GrΓΆΓe |
|
313 | 313 | field_firstname: Vorname |
|
314 | 314 | field_fixed_version: Zielversion |
|
315 | 315 | field_generate_password: Passwort generieren |
|
316 | 316 | field_group_by: Gruppiere Ergebnisse nach |
|
317 | 317 | field_hide_mail: E-Mail-Adresse nicht anzeigen |
|
318 | 318 | field_homepage: Projekt-Homepage |
|
319 | 319 | field_host: Host |
|
320 | 320 | field_hours: Stunden |
|
321 | 321 | field_identifier: Kennung |
|
322 | 322 | field_identity_url: OpenID-URL |
|
323 | 323 | field_inherit_members: Benutzer erben |
|
324 | 324 | field_is_closed: Ticket geschlossen |
|
325 | 325 | field_is_default: Standardeinstellung |
|
326 | 326 | field_is_filter: Als Filter benutzen |
|
327 | 327 | field_is_for_all: FΓΌr alle Projekte |
|
328 | 328 | field_is_in_roadmap: In der Roadmap anzeigen |
|
329 | 329 | field_is_private: Privat |
|
330 | 330 | field_is_public: Γffentlich |
|
331 | 331 | field_is_required: Erforderlich |
|
332 | 332 | field_issue: Ticket |
|
333 | 333 | field_issue_to: ZugehΓΆriges Ticket |
|
334 | 334 | field_issues_visibility: Ticket-Sichtbarkeit |
|
335 | 335 | field_language: Sprache |
|
336 | 336 | field_last_login_on: Letzte Anmeldung |
|
337 | 337 | field_lastname: Nachname |
|
338 | 338 | field_login: Mitgliedsname |
|
339 | 339 | field_mail: E-Mail |
|
340 | 340 | field_mail_notification: Mailbenachrichtigung |
|
341 | 341 | field_max_length: Maximale LΓ€nge |
|
342 | 342 | field_member_of_group: ZustΓ€ndigkeitsgruppe |
|
343 | 343 | field_min_length: Minimale LΓ€nge |
|
344 | 344 | field_multiple: Mehrere Werte |
|
345 | 345 | field_must_change_passwd: Passwort beim nΓ€chsten Login Γ€ndern |
|
346 | 346 | field_name: Name |
|
347 | 347 | field_new_password: Neues Passwort |
|
348 | 348 | field_notes: Kommentare |
|
349 | 349 | field_onthefly: On-the-fly-Benutzererstellung |
|
350 | 350 | field_parent: Unterprojekt von |
|
351 | 351 | field_parent_issue: Γbergeordnete Aufgabe |
|
352 | 352 | field_parent_title: Γbergeordnete Seite |
|
353 | 353 | field_password: Passwort |
|
354 | 354 | field_password_confirmation: BestΓ€tigung |
|
355 | 355 | field_path_to_repository: Pfad zum Repository |
|
356 | 356 | field_port: Port |
|
357 | 357 | field_possible_values: MΓΆgliche Werte |
|
358 | 358 | field_principal: Auftraggeber |
|
359 | 359 | field_priority: PrioritΓ€t |
|
360 | 360 | field_private_notes: Privater Kommentar |
|
361 | 361 | field_project: Projekt |
|
362 | 362 | field_redirect_existing_links: Existierende Links umleiten |
|
363 | 363 | field_regexp: RegulΓ€rer Ausdruck |
|
364 | 364 | field_repository_is_default: Haupt-Repository |
|
365 | 365 | field_role: Rolle |
|
366 | 366 | field_root_directory: Wurzelverzeichnis |
|
367 | 367 | field_scm_path_encoding: Pfad-Kodierung |
|
368 | 368 | field_searchable: Durchsuchbar |
|
369 | 369 | field_sharing: Gemeinsame Verwendung |
|
370 | 370 | field_spent_on: Datum |
|
371 | 371 | field_start_date: Beginn |
|
372 | 372 | field_start_page: Hauptseite |
|
373 | 373 | field_status: Status |
|
374 | 374 | field_subject: Thema |
|
375 | 375 | field_subproject: Unterprojekt von |
|
376 | 376 | field_summary: Zusammenfassung |
|
377 | 377 | field_text: Textfeld |
|
378 | 378 | field_time_entries: Logzeit |
|
379 | 379 | field_time_zone: Zeitzone |
|
380 | 380 | field_timeout: Auszeit (in Sekunden) |
|
381 | 381 | field_title: Titel |
|
382 | 382 | field_tracker: Tracker |
|
383 | 383 | field_type: Typ |
|
384 | 384 | field_updated_on: Aktualisiert |
|
385 | 385 | field_url: URL |
|
386 | 386 | field_user: Benutzer |
|
387 | 387 | field_users_visibility: Benutzer-Sichtbarkeit |
|
388 | 388 | field_value: Wert |
|
389 | 389 | field_version: Version |
|
390 | 390 | field_visible: Sichtbar |
|
391 | 391 | field_warn_on_leaving_unsaved: Vor dem Verlassen einer Seite mit ungesichertem Text im Editor warnen |
|
392 | 392 | field_watcher: Beobachter |
|
393 | 393 | |
|
394 | 394 | general_csv_decimal_separator: ',' |
|
395 | 395 | general_csv_encoding: ISO-8859-1 |
|
396 | 396 | general_csv_separator: ';' |
|
397 | 397 | general_pdf_fontname: freesans |
|
398 | 398 | general_pdf_monospaced_fontname: freemono |
|
399 | 399 | general_first_day_of_week: '1' |
|
400 | 400 | general_lang_name: 'German (Deutsch)' |
|
401 | 401 | general_text_No: 'Nein' |
|
402 | 402 | general_text_Yes: 'Ja' |
|
403 | 403 | general_text_no: 'nein' |
|
404 | 404 | general_text_yes: 'ja' |
|
405 | 405 | |
|
406 | 406 | label_activity: AktivitΓ€t |
|
407 | 407 | label_add_another_file: Eine weitere Datei hinzufΓΌgen |
|
408 | 408 | label_add_note: Kommentar hinzufΓΌgen |
|
409 | 409 | label_add_projects: Projekt hinzufΓΌgen |
|
410 | 410 | label_added: hinzugefΓΌgt |
|
411 | 411 | label_added_time_by: "Von %{author} vor %{age} hinzugefΓΌgt" |
|
412 | 412 | label_additional_workflow_transitions_for_assignee: ZusΓ€tzliche Berechtigungen wenn der Benutzer der Zugewiesene ist |
|
413 | 413 | label_additional_workflow_transitions_for_author: ZusΓ€tzliche Berechtigungen wenn der Benutzer der Autor ist |
|
414 | 414 | label_administration: Administration |
|
415 | 415 | label_age: GeΓ€ndert vor |
|
416 | 416 | label_ago: vor |
|
417 | 417 | label_all: alle |
|
418 | 418 | label_all_time: gesamter Zeitraum |
|
419 | 419 | label_all_words: Alle WΓΆrter |
|
420 | 420 | label_and_its_subprojects: "%{value} und dessen Unterprojekte" |
|
421 | 421 | label_any: alle |
|
422 | 422 | label_any_issues_in_project: irgendein Ticket im Projekt |
|
423 | 423 | label_any_issues_not_in_project: irgendein Ticket nicht im Projekt |
|
424 | 424 | label_api_access_key: API-ZugriffsschlΓΌssel |
|
425 | 425 | label_api_access_key_created_on: Der API-ZugriffsschlΓΌssel wurde vor %{value} erstellt |
|
426 | 426 | label_applied_status: Zugewiesener Status |
|
427 | 427 | label_ascending: Aufsteigend |
|
428 | 428 | label_ask: Nachfragen |
|
429 | 429 | label_assigned_to_me_issues: Mir zugewiesene Tickets |
|
430 | 430 | label_associated_revisions: ZugehΓΆrige Revisionen |
|
431 | 431 | label_attachment: Datei |
|
432 | 432 | label_attachment_delete: Anhang lΓΆschen |
|
433 | 433 | label_attachment_new: Neue Datei |
|
434 | 434 | label_attachment_plural: Dateien |
|
435 | 435 | label_attribute: Attribut |
|
436 | 436 | label_attribute_of_assigned_to: "%{name} des Bearbeiters" |
|
437 | 437 | label_attribute_of_author: "%{name} des Autors" |
|
438 | 438 | label_attribute_of_fixed_version: "%{name} der Zielversion" |
|
439 | 439 | label_attribute_of_issue: "%{name} des Tickets" |
|
440 | 440 | label_attribute_of_project: "%{name} des Projekts" |
|
441 | 441 | label_attribute_of_user: "%{name} des Benutzers" |
|
442 | 442 | label_attribute_plural: Attribute |
|
443 | 443 | label_auth_source: Authentifizierungs-Modus |
|
444 | 444 | label_auth_source_new: Neuer Authentifizierungs-Modus |
|
445 | 445 | label_auth_source_plural: Authentifizierungs-Arten |
|
446 | 446 | label_authentication: Authentifizierung |
|
447 | 447 | label_between: zwischen |
|
448 | 448 | label_blocked_by: Blockiert durch |
|
449 | 449 | label_blocks: Blockiert |
|
450 | 450 | label_board: Forum |
|
451 | 451 | label_board_locked: Gesperrt |
|
452 | 452 | label_board_new: Neues Forum |
|
453 | 453 | label_board_plural: Foren |
|
454 | 454 | label_board_sticky: Wichtig (immer oben) |
|
455 | 455 | label_boolean: Boolean |
|
456 | 456 | label_branch: Zweig |
|
457 | 457 | label_browse: Codebrowser |
|
458 | 458 | label_bulk_edit_selected_issues: Alle ausgewΓ€hlten Tickets bearbeiten |
|
459 | 459 | label_bulk_edit_selected_time_entries: AusgewΓ€hlte ZeitaufwΓ€nde bearbeiten |
|
460 | 460 | label_calendar: Kalender |
|
461 | 461 | label_change_plural: Γnderungen |
|
462 | 462 | label_change_properties: Eigenschaften Γ€ndern |
|
463 | 463 | label_change_status: Statuswechsel |
|
464 | 464 | label_change_view_all: Alle Γnderungen anzeigen |
|
465 | 465 | label_changes_details: Details aller Γnderungen |
|
466 | 466 | label_changeset_plural: Changesets |
|
467 | 467 | label_checkboxes: Checkboxen |
|
468 | 468 | label_check_for_updates: Auf Updates prΓΌfen |
|
469 | 469 | label_child_revision: Nachfolger |
|
470 | 470 | label_chronological_order: in zeitlicher Reihenfolge |
|
471 | 471 | label_close_versions: VollstΓ€ndige Versionen schlieΓen |
|
472 | 472 | label_closed_issues: geschlossen |
|
473 | 473 | label_closed_issues_plural: geschlossen |
|
474 | 474 | label_comment: Kommentar |
|
475 | 475 | label_comment_add: Kommentar hinzufΓΌgen |
|
476 | 476 | label_comment_added: Kommentar hinzugefΓΌgt |
|
477 | 477 | label_comment_delete: Kommentar lΓΆschen |
|
478 | 478 | label_comment_plural: Kommentare |
|
479 | 479 | label_commits_per_author: Γbertragungen pro Autor |
|
480 | 480 | label_commits_per_month: Γbertragungen pro Monat |
|
481 | 481 | label_completed_versions: Abgeschlossene Versionen |
|
482 | 482 | label_confirmation: BestΓ€tigung |
|
483 | 483 | label_contains: enthΓ€lt |
|
484 | 484 | label_copied: kopiert |
|
485 | 485 | label_copied_from: Kopiert von |
|
486 | 486 | label_copied_to: Kopiert nach |
|
487 | 487 | label_copy_attachments: AnhΓ€nge kopieren |
|
488 | 488 | label_copy_same_as_target: So wie das Ziel |
|
489 | 489 | label_copy_source: Quelle |
|
490 | 490 | label_copy_subtasks: Unteraufgaben kopieren |
|
491 | 491 | label_copy_target: Ziel |
|
492 | 492 | label_copy_workflow_from: Workflow kopieren von |
|
493 | 493 | label_cross_project_descendants: Mit Unterprojekten |
|
494 | 494 | label_cross_project_hierarchy: Mit Projekthierarchie |
|
495 | 495 | label_cross_project_system: Mit allen Projekten |
|
496 | 496 | label_cross_project_tree: Mit Projektbaum |
|
497 | 497 | label_current_status: GegenwΓ€rtiger Status |
|
498 | 498 | label_current_version: GegenwΓ€rtige Version |
|
499 | 499 | label_custom_field: Benutzerdefiniertes Feld |
|
500 | 500 | label_custom_field_new: Neues Feld |
|
501 | 501 | label_custom_field_plural: Benutzerdefinierte Felder |
|
502 | 502 | label_custom_field_select_type: Bitte wΓ€hlen Sie den Objekttyp, zu dem das benutzerdefinierte Feld hinzugefΓΌgt werden soll |
|
503 | 503 | label_date: Datum |
|
504 | 504 | label_date_from: Von |
|
505 | 505 | label_date_from_to: von %{start} bis %{end} |
|
506 | 506 | label_date_range: Zeitraum |
|
507 | 507 | label_date_to: Bis |
|
508 | 508 | label_day_plural: Tage |
|
509 | 509 | label_default: Standard |
|
510 | 510 | label_default_columns: Standard-Spalten |
|
511 | 511 | label_deleted: gelΓΆscht |
|
512 | 512 | label_descending: Absteigend |
|
513 | 513 | label_details: Details |
|
514 | 514 | label_diff: diff |
|
515 | 515 | label_diff_inline: einspaltig |
|
516 | 516 | label_diff_side_by_side: nebeneinander |
|
517 | 517 | label_disabled: gesperrt |
|
518 | 518 | label_display: Anzeige |
|
519 | 519 | label_display_per_page: "Pro Seite: %{value}" |
|
520 | 520 | label_display_used_statuses_only: Zeige nur Status an, die von diesem Tracker verwendet werden |
|
521 | 521 | label_document: Dokument |
|
522 | 522 | label_document_added: Dokument hinzugefΓΌgt |
|
523 | 523 | label_document_new: Neues Dokument |
|
524 | 524 | label_document_plural: Dokumente |
|
525 | 525 | label_downloads_abbr: D/L |
|
526 | 526 | label_drop_down_list: Dropdown-Liste |
|
527 | 527 | label_duplicated_by: Dupliziert durch |
|
528 | 528 | label_duplicates: Duplikat von |
|
529 | 529 | label_edit_attachments: AngehΓ€ngte Dateien bearbeiten |
|
530 | 530 | label_enumeration_new: Neuer Wert |
|
531 | 531 | label_enumerations: AufzΓ€hlungen |
|
532 | 532 | label_environment: Umgebung |
|
533 | 533 | label_equals: ist |
|
534 | 534 | label_example: Beispiel |
|
535 | 535 | label_export_options: "%{export_format} Export-Eigenschaften" |
|
536 | 536 | label_export_to: "Auch abrufbar als:" |
|
537 | 537 | label_f_hour: "%{value} Stunde" |
|
538 | 538 | label_f_hour_plural: "%{value} Stunden" |
|
539 | 539 | label_feed_plural: Feeds |
|
540 | 540 | label_feeds_access_key: Atom-ZugriffsschlΓΌssel |
|
541 | 541 | label_feeds_access_key_created_on: "Atom-ZugriffsschlΓΌssel vor %{value} erstellt" |
|
542 | 542 | label_fields_permissions: Feldberechtigungen |
|
543 | 543 | label_file_added: Datei hinzugefΓΌgt |
|
544 | 544 | label_file_plural: Dateien |
|
545 | 545 | label_filter_add: Filter hinzufΓΌgen |
|
546 | 546 | label_filter_plural: Filter |
|
547 | 547 | label_float: FlieΓkommazahl |
|
548 | 548 | label_follows: Nachfolger von |
|
549 | 549 | label_gantt: Gantt-Diagramm |
|
550 | 550 | label_gantt_progress_line: Fortschrittslinie |
|
551 | 551 | label_general: Allgemein |
|
552 | 552 | label_generate_key: Generieren |
|
553 | 553 | label_git_report_last_commit: Bericht des letzten Commits fΓΌr Dateien und Verzeichnisse |
|
554 | 554 | label_greater_or_equal: ">=" |
|
555 | 555 | label_group: Gruppe |
|
556 | 556 | label_group_anonymous: Anonyme Benutzer |
|
557 | 557 | label_group_new: Neue Gruppe |
|
558 | 558 | label_group_non_member: Nichtmitglieder |
|
559 | 559 | label_group_plural: Gruppen |
|
560 | 560 | label_help: Hilfe |
|
561 | 561 | label_hidden: Versteckt |
|
562 | 562 | label_history: Historie |
|
563 | 563 | label_home: Hauptseite |
|
564 | 564 | label_in: in |
|
565 | 565 | label_in_less_than: in weniger als |
|
566 | 566 | label_in_more_than: in mehr als |
|
567 | 567 | label_in_the_next_days: in den nΓ€chsten |
|
568 | 568 | label_in_the_past_days: in den letzten |
|
569 | 569 | label_incoming_emails: Eingehende E-Mails |
|
570 | 570 | label_index_by_date: Seiten nach Datum sortiert |
|
571 | 571 | label_index_by_title: Seiten nach Titel sortiert |
|
572 | 572 | label_information: Information |
|
573 | 573 | label_information_plural: Informationen |
|
574 | 574 | label_integer: Zahl |
|
575 | 575 | label_internal: Intern |
|
576 | 576 | label_issue: Ticket |
|
577 | 577 | label_issue_added: Ticket hinzugefΓΌgt |
|
578 | 578 | label_issue_assigned_to_updated: Bearbeiter aktualisiert |
|
579 | 579 | label_issue_category: Ticket-Kategorie |
|
580 | 580 | label_issue_category_new: Neue Kategorie |
|
581 | 581 | label_issue_category_plural: Ticket-Kategorien |
|
582 | 582 | label_issue_new: Neues Ticket |
|
583 | 583 | label_issue_note_added: Notiz hinzugefΓΌgt |
|
584 | 584 | label_issue_plural: Tickets |
|
585 | 585 | label_issue_priority_updated: PrioritΓ€t aktualisiert |
|
586 | 586 | label_issue_status: Ticket-Status |
|
587 | 587 | label_issue_status_new: Neuer Status |
|
588 | 588 | label_issue_status_plural: Ticket-Status |
|
589 | 589 | label_issue_status_updated: Status aktualisiert |
|
590 | 590 | label_issue_tracking: Tickets |
|
591 | 591 | label_issue_updated: Ticket aktualisiert |
|
592 | 592 | label_issue_view_all: Alle Tickets anzeigen |
|
593 | 593 | label_issue_watchers: Beobachter |
|
594 | 594 | label_issues_by: "Tickets pro %{value}" |
|
595 | 595 | label_issues_visibility_all: Alle Tickets |
|
596 | 596 | label_issues_visibility_own: Tickets die folgender Benutzer erstellt hat oder die ihm zugewiesen sind |
|
597 | 597 | label_issues_visibility_public: Alle ΓΆffentlichen Tickets |
|
598 | 598 | label_item_position: "%{position}/%{count}" |
|
599 | 599 | label_jump_to_a_project: Zu einem Projekt springen... |
|
600 | 600 | label_language_based: SprachabhΓ€ngig |
|
601 | 601 | label_last_changes: "%{count} letzte Γnderungen" |
|
602 | 602 | label_last_login: Letzte Anmeldung |
|
603 | 603 | label_last_month: voriger Monat |
|
604 | 604 | label_last_n_days: "die letzten %{count} Tage" |
|
605 | 605 | label_last_n_weeks: letzte %{count} Wochen |
|
606 | 606 | label_last_week: vorige Woche |
|
607 | 607 | label_latest_compatible_version: Letzte kompatible Version |
|
608 | 608 | label_latest_revision: Aktuellste Revision |
|
609 | 609 | label_latest_revision_plural: Aktuellste Revisionen |
|
610 | 610 | label_ldap_authentication: LDAP-Authentifizierung |
|
611 | 611 | label_less_or_equal: "<=" |
|
612 | 612 | label_less_than_ago: vor weniger als |
|
613 | 613 | label_link: Link |
|
614 | 614 | label_link_copied_issue: Kopierte Tickets verlinken |
|
615 | 615 | label_link_values_to: Werte mit URL verknΓΌpfen |
|
616 | 616 | label_list: Liste |
|
617 | 617 | label_loading: Lade... |
|
618 | 618 | label_logged_as: Angemeldet als |
|
619 | 619 | label_login: Anmelden |
|
620 | 620 | label_login_with_open_id_option: oder mit OpenID anmelden |
|
621 | 621 | label_logout: Abmelden |
|
622 | 622 | label_only: nur |
|
623 | 623 | label_max_size: Maximale GrΓΆΓe |
|
624 | 624 | label_me: ich |
|
625 | 625 | label_member: Mitglied |
|
626 | 626 | label_member_new: Neues Mitglied |
|
627 | 627 | label_member_plural: Mitglieder |
|
628 | 628 | label_message_last: Letzter Forenbeitrag |
|
629 | 629 | label_message_new: Neues Thema |
|
630 | 630 | label_message_plural: ForenbeitrΓ€ge |
|
631 | 631 | label_message_posted: Forenbeitrag hinzugefΓΌgt |
|
632 | 632 | label_min_max_length: LΓ€nge (Min. - Max.) |
|
633 | 633 | label_missing_api_access_key: Der API-ZugriffsschlΓΌssel fehlt. |
|
634 | 634 | label_missing_feeds_access_key: Der Atom-ZugriffsschlΓΌssel fehlt. |
|
635 | 635 | label_modified: geΓ€ndert |
|
636 | 636 | label_module_plural: Module |
|
637 | 637 | label_month: Monat |
|
638 | 638 | label_months_from: Monate ab |
|
639 | 639 | label_more: Mehr |
|
640 | 640 | label_more_than_ago: vor mehr als |
|
641 | 641 | label_my_account: Mein Konto |
|
642 | 642 | label_my_page: Meine Seite |
|
643 | 643 | label_my_page_block: VerfΓΌgbare Widgets |
|
644 | 644 | label_my_projects: Meine Projekte |
|
645 | 645 | label_my_queries: Meine eigenen Abfragen |
|
646 | 646 | label_new: Neu |
|
647 | 647 | label_new_statuses_allowed: Neue Berechtigungen |
|
648 | 648 | label_news: News |
|
649 | 649 | label_news_added: News hinzugefΓΌgt |
|
650 | 650 | label_news_comment_added: Kommentar zu einer News hinzugefΓΌgt |
|
651 | 651 | label_news_latest: Letzte News |
|
652 | 652 | label_news_new: News hinzufΓΌgen |
|
653 | 653 | label_news_plural: News |
|
654 | 654 | label_news_view_all: Alle News anzeigen |
|
655 | 655 | label_next: Weiter |
|
656 | 656 | label_no_change_option: (Keine Γnderung) |
|
657 | 657 | label_no_data: Nichts anzuzeigen |
|
658 | 658 | label_no_preview: Keine Vorschau verfΓΌgbar |
|
659 | 659 | label_no_issues_in_project: keine Tickets im Projekt |
|
660 | 660 | label_nobody: Niemand |
|
661 | 661 | label_none: kein |
|
662 | 662 | label_not_contains: enthΓ€lt nicht |
|
663 | 663 | label_not_equals: ist nicht |
|
664 | 664 | label_open_issues: offen |
|
665 | 665 | label_open_issues_plural: offen |
|
666 | 666 | label_optional_description: Beschreibung (optional) |
|
667 | 667 | label_options: Optionen |
|
668 | 668 | label_overall_activity: AktivitΓ€ten aller Projekte anzeigen |
|
669 | 669 | label_overall_spent_time: Aufgewendete Zeit aller Projekte anzeigen |
|
670 | 670 | label_overview: Γbersicht |
|
671 | 671 | label_parent_revision: VorgΓ€nger |
|
672 | 672 | label_password_lost: Passwort vergessen |
|
673 | 673 | label_password_required: Bitte geben Sie Ihr Passwort ein |
|
674 | 674 | label_permissions: Berechtigungen |
|
675 | 675 | label_permissions_report: BerechtigungsΓΌbersicht |
|
676 | 676 | label_personalize_page: Diese Seite anpassen |
|
677 | 677 | label_planning: Terminplanung |
|
678 | 678 | label_please_login: Anmelden |
|
679 | 679 | label_plugins: Plugins |
|
680 | 680 | label_precedes: VorgΓ€nger von |
|
681 | 681 | label_preferences: PrΓ€ferenzen |
|
682 | 682 | label_preview: Vorschau |
|
683 | 683 | label_previous: ZurΓΌck |
|
684 | 684 | label_principal_search: "Nach Benutzer oder Gruppe suchen:" |
|
685 | 685 | label_profile: Profil |
|
686 | 686 | label_project: Projekt |
|
687 | 687 | label_project_all: Alle Projekte |
|
688 | 688 | label_project_copy_notifications: Sende Mailbenachrichtigungen beim Kopieren des Projekts. |
|
689 | 689 | label_project_latest: Neueste Projekte |
|
690 | 690 | label_project_new: Neues Projekt |
|
691 | 691 | label_project_plural: Projekte |
|
692 | 692 | label_public_projects: Γffentliche Projekte |
|
693 | 693 | label_query: Benutzerdefinierte Abfrage |
|
694 | 694 | label_query_new: Neue Abfrage |
|
695 | 695 | label_query_plural: Benutzerdefinierte Abfragen |
|
696 | 696 | label_radio_buttons: Radio-Buttons |
|
697 | 697 | label_read: Lesen... |
|
698 | 698 | label_readonly: Nur-Lese-Zugriff |
|
699 | 699 | label_register: Registrieren |
|
700 | 700 | label_registered_on: Angemeldet am |
|
701 | 701 | label_registration_activation_by_email: Kontoaktivierung durch E-Mail |
|
702 | 702 | label_registration_automatic_activation: Automatische Kontoaktivierung |
|
703 | 703 | label_registration_manual_activation: Manuelle Kontoaktivierung |
|
704 | 704 | label_related_issues: ZugehΓΆrige Tickets |
|
705 | 705 | label_relates_to: Beziehung mit |
|
706 | 706 | label_relation_delete: Beziehung lΓΆschen |
|
707 | 707 | label_relation_new: Neue Beziehung |
|
708 | 708 | label_renamed: umbenannt |
|
709 | 709 | label_reply_plural: Antworten |
|
710 | 710 | label_report: Bericht |
|
711 | 711 | label_report_plural: Berichte |
|
712 | 712 | label_reported_issues: Erstellte Tickets |
|
713 | 713 | label_repository: Projektarchiv |
|
714 | 714 | label_repository_new: Neues Repository |
|
715 | 715 | label_repository_plural: Projektarchive |
|
716 | 716 | label_required: Erforderlich |
|
717 | 717 | label_result_plural: Resultate |
|
718 | 718 | label_reverse_chronological_order: in umgekehrter zeitlicher Reihenfolge |
|
719 | 719 | label_revision: Revision |
|
720 | 720 | label_revision_id: Revision %{value} |
|
721 | 721 | label_revision_plural: Revisionen |
|
722 | 722 | label_roadmap: Roadmap |
|
723 | 723 | label_roadmap_due_in: "FΓ€llig in %{value}" |
|
724 | 724 | label_roadmap_no_issues: Keine Tickets fΓΌr diese Version |
|
725 | 725 | label_roadmap_overdue: "seit %{value} verspΓ€tet" |
|
726 | 726 | label_role: Rolle |
|
727 | 727 | label_role_and_permissions: Rollen und Rechte |
|
728 | 728 | label_role_anonymous: Anonymous |
|
729 | 729 | label_role_new: Neue Rolle |
|
730 | 730 | label_role_non_member: Nichtmitglied |
|
731 | 731 | label_role_plural: Rollen |
|
732 | 732 | label_scm: Versionskontrollsystem |
|
733 | 733 | label_search: Suche |
|
734 | 734 | label_search_for_watchers: Nach hinzufΓΌgbaren Beobachtern suchen |
|
735 | 735 | label_search_titles_only: Nur Titel durchsuchen |
|
736 | 736 | label_send_information: Sende Kontoinformationen an Benutzer |
|
737 | 737 | label_send_test_email: Test-E-Mail senden |
|
738 | 738 | label_session_expiration: Ende einer Sitzung |
|
739 | 739 | label_settings: Konfiguration |
|
740 | 740 | label_show_closed_projects: Geschlossene Projekte anzeigen |
|
741 | 741 | label_show_completed_versions: Abgeschlossene Versionen anzeigen |
|
742 | 742 | label_sort: Sortierung |
|
743 | 743 | label_sort_by: "Sortiert nach %{value}" |
|
744 | 744 | label_sort_higher: Eins hΓΆher |
|
745 | 745 | label_sort_highest: An den Anfang |
|
746 | 746 | label_sort_lower: Eins tiefer |
|
747 | 747 | label_sort_lowest: Ans Ende |
|
748 | 748 | label_spent_time: Aufgewendete Zeit |
|
749 | 749 | label_statistics: Statistiken |
|
750 | 750 | label_status_transitions: StatusΓ€nderungen |
|
751 | 751 | label_stay_logged_in: Angemeldet bleiben |
|
752 | 752 | label_string: Text |
|
753 | 753 | label_subproject_new: Neues Unterprojekt |
|
754 | 754 | label_subproject_plural: Unterprojekte |
|
755 | 755 | label_subtask_plural: Unteraufgaben |
|
756 | 756 | label_tag: Markierung |
|
757 | 757 | label_text: Langer Text |
|
758 | 758 | label_theme: Design-Stil |
|
759 | 759 | label_this_month: aktueller Monat |
|
760 | 760 | label_this_week: aktuelle Woche |
|
761 | 761 | label_this_year: aktuelles Jahr |
|
762 | 762 | label_time_entry_plural: BenΓΆtigte Zeit |
|
763 | 763 | label_time_tracking: Zeiterfassung |
|
764 | 764 | label_today: heute |
|
765 | 765 | label_topic_plural: Themen |
|
766 | 766 | label_total: Gesamtzahl |
|
767 | 767 | label_total_time: Gesamtzeit |
|
768 | 768 | label_tracker: Tracker |
|
769 | 769 | label_tracker_new: Neuer Tracker |
|
770 | 770 | label_tracker_plural: Tracker |
|
771 | 771 | label_unknown_plugin: Unbekanntes Plugin |
|
772 | 772 | label_update_issue_done_ratios: Ticket-Fortschritt aktualisieren |
|
773 | 773 | label_updated_time: "Vor %{value} aktualisiert" |
|
774 | 774 | label_updated_time_by: "Von %{author} vor %{age} aktualisiert" |
|
775 | 775 | label_used_by: Benutzt von |
|
776 | 776 | label_user: Benutzer |
|
777 | 777 | label_user_activity: "AktivitΓ€t von %{value}" |
|
778 | 778 | label_user_anonymous: Anonym |
|
779 | 779 | label_user_mail_no_self_notified: "Ich mΓΆchte nicht ΓΌber Γnderungen benachrichtigt werden, die ich selbst durchfΓΌhre." |
|
780 | 780 | label_user_mail_option_all: "FΓΌr alle Ereignisse in all meinen Projekten" |
|
781 | 781 | label_user_mail_option_none: Keine Ereignisse |
|
782 | 782 | label_user_mail_option_only_assigned: Nur fΓΌr Aufgaben fΓΌr die ich zustΓ€ndig bin |
|
783 | 783 | label_user_mail_option_only_my_events: Nur fΓΌr Aufgaben die ich beobachte oder an welchen ich mitarbeite |
|
784 | 784 | label_user_mail_option_only_owner: Nur fΓΌr Aufgaben die ich angelegt habe |
|
785 | 785 | label_user_mail_option_selected: "FΓΌr alle Ereignisse in den ausgewΓ€hlten Projekten" |
|
786 | 786 | label_user_new: Neuer Benutzer |
|
787 | 787 | label_user_plural: Benutzer |
|
788 | 788 | label_user_search: "Nach Benutzer suchen:" |
|
789 | 789 | label_users_visibility_all: Alle aktiven Benutzer |
|
790 | 790 | label_users_visibility_members_of_visible_projects: Mitglieder von sichtbaren Projekten |
|
791 | 791 | label_version: Version |
|
792 | 792 | label_version_new: Neue Version |
|
793 | 793 | label_version_plural: Versionen |
|
794 | 794 | label_version_sharing_descendants: Mit Unterprojekten |
|
795 | 795 | label_version_sharing_hierarchy: Mit Projekthierarchie |
|
796 | 796 | label_version_sharing_none: Nicht gemeinsam verwenden |
|
797 | 797 | label_version_sharing_system: Mit allen Projekten |
|
798 | 798 | label_version_sharing_tree: Mit Projektbaum |
|
799 | 799 | label_view_all_revisions: Alle Revisionen anzeigen |
|
800 | 800 | label_view_diff: Unterschiede anzeigen |
|
801 | 801 | label_view_revisions: Revisionen anzeigen |
|
802 | 802 | label_visibility_private: nur fΓΌr mich |
|
803 | 803 | label_visibility_public: fΓΌr jeden Benutzer |
|
804 | 804 | label_visibility_roles: nur fΓΌr diese Rollen |
|
805 | 805 | label_watched_issues: Beobachtete Tickets |
|
806 | 806 | label_week: Woche |
|
807 | 807 | label_wiki: Wiki |
|
808 | 808 | label_wiki_content_added: Wiki-Seite hinzugefΓΌgt |
|
809 | 809 | label_wiki_content_updated: Wiki-Seite aktualisiert |
|
810 | 810 | label_wiki_edit: Wiki-Bearbeitung |
|
811 | 811 | label_wiki_edit_plural: Wiki-Bearbeitungen |
|
812 | 812 | label_wiki_page: Wiki-Seite |
|
813 | 813 | label_wiki_page_plural: Wiki-Seiten |
|
814 | 814 | label_wiki_page_new: Neue Wiki-Seite |
|
815 | 815 | label_workflow: Workflow |
|
816 | 816 | label_x_closed_issues_abbr: |
|
817 | 817 | zero: 0 geschlossen |
|
818 | 818 | one: 1 geschlossen |
|
819 | 819 | other: "%{count} geschlossen" |
|
820 | 820 | label_x_comments: |
|
821 | 821 | zero: keine Kommentare |
|
822 | 822 | one: 1 Kommentar |
|
823 | 823 | other: "%{count} Kommentare" |
|
824 | 824 | label_x_issues: |
|
825 | 825 | zero: 0 Tickets |
|
826 | 826 | one: 1 Ticket |
|
827 | 827 | other: "%{count} Tickets" |
|
828 | 828 | label_x_open_issues_abbr: |
|
829 | 829 | zero: 0 offen |
|
830 | 830 | one: 1 offen |
|
831 | 831 | other: "%{count} offen" |
|
832 | 832 | label_x_projects: |
|
833 | 833 | zero: keine Projekte |
|
834 | 834 | one: 1 Projekt |
|
835 | 835 | other: "%{count} Projekte" |
|
836 | 836 | label_year: Jahr |
|
837 | 837 | label_yesterday: gestern |
|
838 | 838 | |
|
839 | 839 | mail_body_account_activation_request: "Ein neuer Benutzer (%{value}) hat sich registriert. Sein Konto wartet auf Ihre Genehmigung:" |
|
840 | 840 | mail_body_account_information: Ihre Konto-Informationen |
|
841 | 841 | mail_body_account_information_external: "Sie kΓΆnnen sich mit Ihrem Konto %{value} anmelden." |
|
842 | 842 | mail_body_lost_password: 'Benutzen Sie den folgenden Link, um Ihr Passwort zu Γ€ndern:' |
|
843 | 843 | mail_body_register: 'Um Ihr Konto zu aktivieren, benutzen Sie folgenden Link:' |
|
844 | 844 | mail_body_reminder: "%{count} Tickets, die Ihnen zugewiesen sind, mΓΌssen in den nΓ€chsten %{days} Tagen abgegeben werden:" |
|
845 | 845 | mail_body_wiki_content_added: "Die Wiki-Seite '%{id}' wurde von %{author} hinzugefΓΌgt." |
|
846 | 846 | mail_body_wiki_content_updated: "Die Wiki-Seite '%{id}' wurde von %{author} aktualisiert." |
|
847 | 847 | mail_subject_account_activation_request: "Antrag auf %{value} Kontoaktivierung" |
|
848 | 848 | mail_subject_lost_password: "Ihr %{value} Passwort" |
|
849 | 849 | mail_subject_register: "%{value} Kontoaktivierung" |
|
850 | 850 | mail_subject_reminder: "%{count} Tickets mΓΌssen in den nΓ€chsten %{days} Tagen abgegeben werden" |
|
851 | 851 | mail_subject_wiki_content_added: "Wiki-Seite '%{id}' hinzugefΓΌgt" |
|
852 | 852 | mail_subject_wiki_content_updated: "Wiki-Seite '%{id}' erfolgreich aktualisiert" |
|
853 | 853 | mail_subject_security_notification: "Sicherheitshinweis" |
|
854 | 854 | mail_body_security_notification_change: "%{field} wurde geΓ€ndert." |
|
855 | 855 | mail_body_security_notification_change_to: "%{field} wurde geΓ€ndert zu %{value}." |
|
856 | 856 | mail_body_security_notification_add: "%{field} %{value} wurde hinzugefΓΌgt." |
|
857 | 857 | mail_body_security_notification_remove: "%{field} %{value} wurde entfernt." |
|
858 | 858 | mail_body_security_notification_notify_enabled: "E-Mail-Adresse %{value} erhΓ€lt nun Benachrichtigungen." |
|
859 | 859 | mail_body_security_notification_notify_disabled: "E-Mail-Adresse %{value} erhΓ€lt keine Benachrichtigungen mehr." |
|
860 | 860 | |
|
861 | 861 | notice_account_activated: Ihr Konto ist aktiviert. Sie kΓΆnnen sich jetzt anmelden. |
|
862 | 862 | notice_account_deleted: Ihr Benutzerkonto wurde unwiderruflich gelΓΆscht. |
|
863 | 863 | notice_account_invalid_credentials: Benutzer oder Passwort ist ungΓΌltig. |
|
864 | 864 | notice_account_lost_email_sent: Eine E-Mail mit Anweisungen, ein neues Passwort zu wΓ€hlen, wurde Ihnen geschickt. |
|
865 | 865 | notice_account_locked: Ihr Konto ist gesperrt. |
|
866 | 866 | notice_account_not_activated_yet: Sie haben Ihr Konto noch nicht aktiviert. Wenn Sie die Aktivierungsmail erneut erhalten wollen, <a href="%{url}">klicken Sie bitte hier</a>. |
|
867 | 867 | notice_account_password_updated: Passwort wurde erfolgreich aktualisiert. |
|
868 | 868 | notice_account_pending: "Ihr Konto wurde erstellt und wartet jetzt auf die Genehmigung des Administrators." |
|
869 | 869 | notice_account_register_done: Konto wurde erfolgreich angelegt. Eine E-Mail mit weiteren Instruktionen zur Kontoaktivierung wurde an %{email} gesendet. |
|
870 | 870 | notice_account_unknown_email: Unbekannter Benutzer. |
|
871 | 871 | notice_account_updated: Konto wurde erfolgreich aktualisiert. |
|
872 | 872 | notice_account_wrong_password: Falsches Passwort. |
|
873 | 873 | notice_api_access_key_reseted: Ihr API-ZugriffsschlΓΌssel wurde zurΓΌckgesetzt. |
|
874 | 874 | notice_can_t_change_password: Dieses Konto verwendet eine externe Authentifizierungs-Quelle. UnmΓΆglich, das Passwort zu Γ€ndern. |
|
875 | 875 | notice_default_data_loaded: Die Standard-Konfiguration wurde erfolgreich geladen. |
|
876 | 876 | notice_email_error: "Beim Senden einer E-Mail ist ein Fehler aufgetreten (%{value})." |
|
877 | 877 | notice_email_sent: "Eine E-Mail wurde an %{value} gesendet." |
|
878 | 878 | notice_failed_to_save_issues: "%{count} von %{total} ausgewΓ€hlten Tickets konnte(n) nicht gespeichert werden: %{ids}." |
|
879 | 879 | notice_failed_to_save_members: "Benutzer konnte nicht gespeichert werden: %{errors}." |
|
880 | 880 | notice_failed_to_save_time_entries: "Gescheitert %{count} ZeiteintrΓ€ge fΓΌr %{total} von ausgewΓ€hlten: %{ids} zu speichern." |
|
881 | 881 | notice_feeds_access_key_reseted: Ihr Atom-ZugriffsschlΓΌssel wurde zurΓΌckgesetzt. |
|
882 | 882 | notice_file_not_found: Anhang existiert nicht oder ist gelΓΆscht worden. |
|
883 | 883 | notice_gantt_chart_truncated: Die Grafik ist unvollstΓ€ndig, da das Maximum der anzeigbaren Aufgaben ΓΌberschritten wurde (%{max}) |
|
884 | 884 | notice_issue_done_ratios_updated: Der Ticket-Fortschritt wurde aktualisiert. |
|
885 | 885 | notice_issue_successful_create: Ticket %{id} erstellt. |
|
886 | 886 | notice_issue_update_conflict: Das Ticket wurde wΓ€hrend Ihrer Bearbeitung von einem anderen Nutzer ΓΌberarbeitet. |
|
887 | 887 | notice_locking_conflict: Datum wurde von einem anderen Benutzer geΓ€ndert. |
|
888 | 888 | notice_new_password_must_be_different: Das neue Passwort muss sich vom dem Aktuellen unterscheiden |
|
889 | 889 | notice_no_issue_selected: "Kein Ticket ausgewΓ€hlt! Bitte wΓ€hlen Sie die Tickets, die Sie bearbeiten mΓΆchten." |
|
890 | 890 | notice_not_authorized: Sie sind nicht berechtigt, auf diese Seite zuzugreifen. |
|
891 | 891 | notice_not_authorized_archived_project: Das Projekt wurde archiviert und ist daher nicht nicht verfΓΌgbar. |
|
892 | 892 | notice_successful_connection: Verbindung erfolgreich. |
|
893 | 893 | notice_successful_create: Erfolgreich angelegt |
|
894 | 894 | notice_successful_delete: Erfolgreich gelΓΆscht. |
|
895 | 895 | notice_successful_update: Erfolgreich aktualisiert. |
|
896 | 896 | notice_unable_delete_time_entry: Der Zeiterfassungseintrag konnte nicht gelΓΆscht werden. |
|
897 | 897 | notice_unable_delete_version: Die Version konnte nicht gelΓΆscht werden. |
|
898 | 898 | notice_user_successful_create: Benutzer %{id} angelegt. |
|
899 | 899 | |
|
900 | 900 | permission_add_issue_notes: Kommentare hinzufΓΌgen |
|
901 | 901 | permission_add_issue_watchers: Beobachter hinzufΓΌgen |
|
902 | 902 | permission_add_issues: Tickets hinzufΓΌgen |
|
903 | 903 | permission_add_messages: ForenbeitrΓ€ge hinzufΓΌgen |
|
904 | 904 | permission_add_project: Projekt erstellen |
|
905 | 905 | permission_add_subprojects: Unterprojekte erstellen |
|
906 | 906 | permission_add_documents: Dokumente hinzufΓΌgen |
|
907 | 907 | permission_browse_repository: Projektarchiv ansehen |
|
908 | 908 | permission_close_project: SchlieΓen / erneutes Γffnen eines Projekts |
|
909 | 909 | permission_comment_news: News kommentieren |
|
910 | 910 | permission_commit_access: Commit-Zugriff |
|
911 | 911 | permission_delete_issue_watchers: Beobachter lΓΆschen |
|
912 | 912 | permission_delete_issues: Tickets lΓΆschen |
|
913 | 913 | permission_delete_messages: ForenbeitrΓ€ge lΓΆschen |
|
914 | 914 | permission_delete_own_messages: Eigene ForenbeitrΓ€ge lΓΆschen |
|
915 | 915 | permission_delete_wiki_pages: Wiki-Seiten lΓΆschen |
|
916 | 916 | permission_delete_wiki_pages_attachments: AnhΓ€nge lΓΆschen |
|
917 | 917 | permission_delete_documents: Dokumente lΓΆschen |
|
918 | 918 | permission_edit_issue_notes: Kommentare bearbeiten |
|
919 | 919 | permission_edit_issues: Tickets bearbeiten |
|
920 | 920 | permission_edit_messages: ForenbeitrΓ€ge bearbeiten |
|
921 | 921 | permission_edit_own_issue_notes: Eigene Kommentare bearbeiten |
|
922 | 922 | permission_edit_own_messages: Eigene ForenbeitrΓ€ge bearbeiten |
|
923 | 923 | permission_edit_own_time_entries: Selbst gebuchte AufwΓ€nde bearbeiten |
|
924 | 924 | permission_edit_project: Projekt bearbeiten |
|
925 | 925 | permission_edit_time_entries: Gebuchte AufwΓ€nde bearbeiten |
|
926 | 926 | permission_edit_wiki_pages: Wiki-Seiten bearbeiten |
|
927 | 927 | permission_edit_documents: Dokumente bearbeiten |
|
928 | 928 | permission_export_wiki_pages: Wiki-Seiten exportieren |
|
929 | 929 | permission_log_time: AufwΓ€nde buchen |
|
930 | 930 | permission_manage_boards: Foren verwalten |
|
931 | 931 | permission_manage_categories: Ticket-Kategorien verwalten |
|
932 | 932 | permission_manage_files: Dateien verwalten |
|
933 | 933 | permission_manage_issue_relations: Ticket-Beziehungen verwalten |
|
934 | 934 | permission_manage_members: Mitglieder verwalten |
|
935 | 935 | permission_manage_news: News verwalten |
|
936 | 936 | permission_manage_project_activities: AktivitΓ€ten (Zeiterfassung) verwalten |
|
937 | 937 | permission_manage_public_queries: Γffentliche Filter verwalten |
|
938 | 938 | permission_manage_related_issues: ZugehΓΆrige Tickets verwalten |
|
939 | 939 | permission_manage_repository: Projektarchiv verwalten |
|
940 | 940 | permission_manage_subtasks: Unteraufgaben verwalten |
|
941 | 941 | permission_manage_versions: Versionen verwalten |
|
942 | 942 | permission_manage_wiki: Wiki verwalten |
|
943 | 943 | permission_move_issues: Tickets verschieben |
|
944 | 944 | permission_protect_wiki_pages: Wiki-Seiten schΓΌtzen |
|
945 | 945 | permission_rename_wiki_pages: Wiki-Seiten umbenennen |
|
946 | 946 | permission_save_queries: Filter speichern |
|
947 | 947 | permission_select_project_modules: Projektmodule auswΓ€hlen |
|
948 | 948 | permission_set_issues_private: Tickets privat oder ΓΆffentlich markieren |
|
949 | 949 | permission_set_notes_private: Kommentar als privat markieren |
|
950 | 950 | permission_set_own_issues_private: Eigene Tickets privat oder ΓΆffentlich markieren |
|
951 | 951 | permission_view_calendar: Kalender ansehen |
|
952 | 952 | permission_view_changesets: Changesets ansehen |
|
953 | 953 | permission_view_documents: Dokumente ansehen |
|
954 | 954 | permission_view_files: Dateien ansehen |
|
955 | 955 | permission_view_gantt: Gantt-Diagramm ansehen |
|
956 | 956 | permission_view_issue_watchers: Liste der Beobachter ansehen |
|
957 | 957 | permission_view_issues: Tickets anzeigen |
|
958 | 958 | permission_view_messages: ForenbeitrΓ€ge ansehen |
|
959 | 959 | permission_view_private_notes: Private Kommentare sehen |
|
960 | 960 | permission_view_time_entries: Gebuchte AufwΓ€nde ansehen |
|
961 | 961 | permission_view_wiki_edits: Wiki-Versionsgeschichte ansehen |
|
962 | 962 | permission_view_wiki_pages: Wiki ansehen |
|
963 | 963 | |
|
964 | 964 | project_module_boards: Foren |
|
965 | 965 | project_module_calendar: Kalender |
|
966 | 966 | project_module_documents: Dokumente |
|
967 | 967 | project_module_files: Dateien |
|
968 | 968 | project_module_gantt: Gantt |
|
969 | 969 | project_module_issue_tracking: Ticket-Verfolgung |
|
970 | 970 | project_module_news: News |
|
971 | 971 | project_module_repository: Projektarchiv |
|
972 | 972 | project_module_time_tracking: Zeiterfassung |
|
973 | 973 | project_module_wiki: Wiki |
|
974 | 974 | project_status_active: aktiv |
|
975 | 975 | project_status_archived: archiviert |
|
976 | 976 | project_status_closed: geschlossen |
|
977 | 977 | |
|
978 | 978 | setting_activity_days_default: Anzahl Tage pro Seite der Projekt-AktivitΓ€t |
|
979 | 979 | setting_app_subtitle: Applikations-Untertitel |
|
980 | 980 | setting_app_title: Applikations-Titel |
|
981 | 981 | setting_attachment_max_size: Max. DateigrΓΆΓe |
|
982 | 982 | setting_autofetch_changesets: Changesets automatisch abrufen |
|
983 | 983 | setting_autologin: Automatische Anmeldung lΓ€uft ab nach |
|
984 | 984 | setting_bcc_recipients: E-Mails als Blindkopie (BCC) senden |
|
985 | 985 | setting_cache_formatted_text: Formatierten Text im Cache speichern |
|
986 | 986 | setting_commit_cross_project_ref: Erlauben auf Tickets aller anderen Projekte zu referenzieren |
|
987 | 987 | setting_commit_fix_keywords: SchlΓΌsselwΓΆrter (Status) |
|
988 | 988 | setting_commit_logtime_activity_id: AktivitΓ€t fΓΌr die Zeiterfassung |
|
989 | 989 | setting_commit_logtime_enabled: Aktiviere Zeiterfassung via Commit-Nachricht |
|
990 | 990 | setting_commit_ref_keywords: SchlΓΌsselwΓΆrter (Beziehungen) |
|
991 | 991 | setting_cross_project_issue_relations: Ticket-Beziehungen zwischen Projekten erlauben |
|
992 | 992 | setting_cross_project_subtasks: ProjektΓΌbergreifende Unteraufgaben erlauben |
|
993 | 993 | setting_date_format: Datumsformat |
|
994 | 994 | setting_default_issue_start_date_to_creation_date: Aktuelles Datum als Beginn fΓΌr neue Tickets verwenden |
|
995 | 995 | setting_default_language: Standardsprache |
|
996 | 996 | setting_default_notification_option: Standard Benachrichtigungsoptionen |
|
997 | 997 | setting_default_projects_modules: StandardmΓ€Γig aktivierte Module fΓΌr neue Projekte |
|
998 | 998 | setting_default_projects_public: Neue Projekte sind standardmΓ€Γig ΓΆffentlich |
|
999 | 999 | setting_default_projects_tracker_ids: StandardmΓ€Γig aktivierte Tracker fΓΌr neue Projekte |
|
1000 | 1000 | setting_diff_max_lines_displayed: Maximale Anzahl anzuzeigender Diff-Zeilen |
|
1001 | 1001 | setting_display_subprojects_issues: Tickets von Unterprojekten im Hauptprojekt anzeigen |
|
1002 | 1002 | setting_emails_footer: E-Mail-FuΓzeile |
|
1003 | 1003 | setting_emails_header: E-Mail-Kopfzeile |
|
1004 | 1004 | setting_enabled_scm: Aktivierte Versionskontrollsysteme |
|
1005 | 1005 | setting_feeds_limit: Max. Anzahl EintrΓ€ge pro Atom-Feed |
|
1006 | 1006 | setting_file_max_size_displayed: Maximale GrΓΆΓe inline angezeigter Textdateien |
|
1007 | 1007 | setting_force_default_language_for_anonymous: Standardsprache fΓΌr anonyme Benutzer erzwingen |
|
1008 | 1008 | setting_force_default_language_for_loggedin: Standardsprache fΓΌr angemeldete Benutzer erzwingen |
|
1009 | 1009 | setting_gantt_items_limit: Maximale Anzahl von Aufgaben die im Gantt-Chart angezeigt werden |
|
1010 | 1010 | setting_gravatar_default: Standard-Gravatar-Bild |
|
1011 | 1011 | setting_gravatar_enabled: Gravatar-Benutzerbilder benutzen |
|
1012 | 1012 | setting_host_name: Hostname |
|
1013 | 1013 | setting_issue_done_ratio: Berechne den Ticket-Fortschritt mittels |
|
1014 | 1014 | setting_issue_done_ratio_issue_field: Ticket-Feld %-erledigt |
|
1015 | 1015 | setting_issue_done_ratio_issue_status: Ticket-Status |
|
1016 | 1016 | setting_issue_group_assignment: Ticketzuweisung an Gruppen erlauben |
|
1017 | 1017 | setting_issue_list_default_columns: Standard-Spalten in der Ticket-Auflistung |
|
1018 | 1018 | setting_issues_export_limit: Max. Anzahl Tickets bei CSV/PDF-Export |
|
1019 | 1019 | setting_jsonp_enabled: JSONP UnterstΓΌtzung aktivieren |
|
1020 | 1020 | setting_link_copied_issue: Tickets beim kopieren verlinken |
|
1021 | 1021 | setting_login_required: Authentifizierung erforderlich |
|
1022 | 1022 | setting_mail_from: E-Mail-Absender |
|
1023 | 1023 | setting_mail_handler_api_enabled: Abruf eingehender E-Mails aktivieren |
|
1024 | 1024 | setting_mail_handler_api_key: API-SchlΓΌssel fΓΌr eingehende E-Mails |
|
1025 | 1025 | setting_sys_api_key: API-SchlΓΌssel fΓΌr Webservice zur Projektarchiv-Verwaltung |
|
1026 | 1026 | setting_mail_handler_body_delimiters: "Schneide E-Mails nach einer dieser Zeilen ab" |
|
1027 | 1027 | setting_mail_handler_excluded_filenames: AnhΓ€nge nach Namen ausschlieΓen |
|
1028 | 1028 | setting_new_project_user_role_id: Rolle, die einem Nicht-Administrator zugeordnet wird, der ein Projekt erstellt |
|
1029 | 1029 | setting_non_working_week_days: Arbeitsfreie Tage |
|
1030 | 1030 | setting_openid: Erlaube OpenID-Anmeldung und -Registrierung |
|
1031 | 1031 | setting_password_min_length: MindestlΓ€nge des Passworts |
|
1032 | 1032 | setting_password_max_age: Erzwinge Passwortwechsel nach |
|
1033 | 1033 | setting_lost_password: Erlaube Passwort-ZurΓΌcksetzen per E-Mail |
|
1034 | 1034 | setting_per_page_options: Objekte pro Seite |
|
1035 | 1035 | setting_plain_text_mail: Nur reinen Text (kein HTML) senden |
|
1036 | 1036 | setting_protocol: Protokoll |
|
1037 | 1037 | setting_repositories_encodings: Kodierung von AnhΓ€ngen und Repositories |
|
1038 | 1038 | setting_repository_log_display_limit: Maximale Anzahl anzuzeigender Revisionen in der Historie einer Datei |
|
1039 | 1039 | setting_rest_api_enabled: REST-Schnittstelle aktivieren |
|
1040 | 1040 | setting_self_registration: Registrierung ermΓΆglichen |
|
1041 | 1041 | setting_sequential_project_identifiers: Fortlaufende Projektkennungen generieren |
|
1042 | 1042 | setting_session_lifetime: LΓ€ngste Dauer einer Sitzung |
|
1043 | 1043 | setting_session_timeout: ZeitΓΌberschreitung bei InaktivitΓ€t |
|
1044 | 1044 | setting_start_of_week: Wochenanfang |
|
1045 | 1045 | setting_sys_api_enabled: Webservice zur Verwaltung der Projektarchive benutzen |
|
1046 | 1046 | setting_text_formatting: Textformatierung |
|
1047 | 1047 | setting_thumbnails_enabled: Vorschaubilder von DateianhΓ€ngen anzeigen |
|
1048 | 1048 | setting_thumbnails_size: GrΓΆΓe der Vorschaubilder (in Pixel) |
|
1049 | 1049 | setting_time_format: Zeitformat |
|
1050 | setting_timespan_format: Format fΓΌr Zeitspannen | |
|
1050 | 1051 | setting_unsubscribe: Erlaubt Benutzern das eigene Benutzerkonto zu lΓΆschen |
|
1051 | 1052 | setting_user_format: Benutzer-Anzeigeformat |
|
1052 | 1053 | setting_welcome_text: Willkommenstext |
|
1053 | 1054 | setting_wiki_compression: Wiki-Historie komprimieren |
|
1054 | 1055 | |
|
1055 | 1056 | status_active: aktiv |
|
1056 | 1057 | status_locked: gesperrt |
|
1057 | 1058 | status_registered: nicht aktivierte |
|
1058 | 1059 | |
|
1059 | 1060 | text_account_destroy_confirmation: "MΓΆchten Sie wirklich fortfahren?\nIhr Benutzerkonto wird fΓΌr immer gelΓΆscht und kann nicht wiederhergestellt werden." |
|
1060 | 1061 | text_are_you_sure: Sind Sie sicher? |
|
1061 | 1062 | text_assign_time_entries_to_project: Gebuchte AufwΓ€nde dem Projekt zuweisen |
|
1062 | 1063 | text_caracters_maximum: "Max. %{count} Zeichen." |
|
1063 | 1064 | text_caracters_minimum: "Muss mindestens %{count} Zeichen lang sein." |
|
1064 | 1065 | text_comma_separated: Mehrere Werte erlaubt (durch Komma getrennt). |
|
1065 | 1066 | text_convert_available: ImageMagick-Konvertierung verfΓΌgbar (optional) |
|
1066 | 1067 | text_custom_field_possible_values_info: 'Eine Zeile pro Wert' |
|
1067 | 1068 | text_default_administrator_account_changed: Administrator-Passwort geΓ€ndert |
|
1068 | 1069 | text_destroy_time_entries: Gebuchte AufwΓ€nde lΓΆschen |
|
1069 | 1070 | text_destroy_time_entries_question: Es wurden bereits %{hours} Stunden auf dieses Ticket gebucht. Was soll mit den AufwΓ€nden geschehen? |
|
1070 | 1071 | text_diff_truncated: '... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen ΓΌberschreitet.' |
|
1071 | 1072 | text_email_delivery_not_configured: "Der SMTP-Server ist nicht konfiguriert und Mailbenachrichtigungen sind ausgeschaltet.\nNehmen Sie die Einstellungen fΓΌr Ihren SMTP-Server in config/configuration.yml vor und starten Sie die Applikation neu." |
|
1072 | 1073 | text_enumeration_category_reassign_to: 'Die Objekte stattdessen diesem Wert zuordnen:' |
|
1073 | 1074 | text_enumeration_destroy_question: "%{count} Objekt(e) sind diesem Wert zugeordnet." |
|
1074 | 1075 | text_file_repository_writable: Verzeichnis fΓΌr Dateien beschreibbar |
|
1075 | 1076 | text_git_repository_note: Repository steht fΓΌr sich alleine (bare) und liegt lokal (z.B. /gitrepo, c:\gitrepo) |
|
1076 | 1077 | text_issue_added: "Ticket %{id} wurde erstellt von %{author}." |
|
1077 | 1078 | text_issue_category_destroy_assignments: Kategorie-Zuordnung entfernen |
|
1078 | 1079 | text_issue_category_destroy_question: "Einige Tickets (%{count}) sind dieser Kategorie zugeodnet. Was mΓΆchten Sie tun?" |
|
1079 | 1080 | text_issue_category_reassign_to: Tickets dieser Kategorie zuordnen |
|
1080 | 1081 | text_issue_conflict_resolution_add_notes: Meine Γnderungen ΓΌbernehmen und alle anderen Γnderungen verwerfen |
|
1081 | 1082 | text_issue_conflict_resolution_cancel: Meine Γnderungen verwerfen und %{link} neu anzeigen |
|
1082 | 1083 | text_issue_conflict_resolution_overwrite: Meine Γnderungen trotzdem ΓΌbernehmen (vorherige Notizen bleiben erhalten aber manche kΓΆnnen ΓΌberschrieben werden) |
|
1083 | 1084 | text_issue_updated: "Ticket %{id} wurde aktualisiert von %{author}." |
|
1084 | 1085 | text_issues_destroy_confirmation: 'Sind Sie sicher, dass Sie die ausgewΓ€hlten Tickets lΓΆschen mΓΆchten?' |
|
1085 | 1086 | text_issues_destroy_descendants_confirmation: Dies wird auch %{count} Unteraufgabe/n lΓΆschen. |
|
1086 | 1087 | text_issues_ref_in_commit_messages: Ticket-Beziehungen und -Status in Commit-Nachrichten |
|
1087 | 1088 | text_journal_added: "%{label} %{value} wurde hinzugefΓΌgt" |
|
1088 | 1089 | text_journal_changed: "%{label} wurde von %{old} zu %{new} geΓ€ndert" |
|
1089 | 1090 | text_journal_changed_no_detail: "%{label} aktualisiert" |
|
1090 | 1091 | text_journal_deleted: "%{label} %{old} wurde gelΓΆscht" |
|
1091 | 1092 | text_journal_set_to: "%{label} wurde auf %{value} gesetzt" |
|
1092 | 1093 | text_length_between: "LΓ€nge zwischen %{min} und %{max} Zeichen." |
|
1093 | 1094 | text_line_separated: Mehrere Werte sind erlaubt (eine Zeile pro Wert). |
|
1094 | 1095 | text_load_default_configuration: Standard-Konfiguration laden |
|
1095 | 1096 | text_mercurial_repository_note: Lokales repository (e.g. /hgrepo, c:\hgrepo) |
|
1096 | 1097 | text_min_max_length_info: 0 heiΓt keine BeschrΓ€nkung |
|
1097 | 1098 | text_no_configuration_data: "Rollen, Tracker, Ticket-Status und Workflows wurden noch nicht konfiguriert.\nEs ist sehr zu empfehlen, die Standard-Konfiguration zu laden. Sobald sie geladen ist, kΓΆnnen Sie diese abΓ€ndern." |
|
1098 | 1099 | text_own_membership_delete_confirmation: "Sie sind dabei, einige oder alle Ihre Berechtigungen zu entfernen. Es ist mΓΆglich, dass Sie danach das Projekt nicht mehr ansehen oder bearbeiten dΓΌrfen.\nSind Sie sicher, dass Sie dies tun mΓΆchten?" |
|
1099 | 1100 | text_plugin_assets_writable: Verzeichnis fΓΌr Plugin-Assets beschreibbar |
|
1100 | 1101 | text_project_closed: Dieses Projekt ist geschlossen und kann nicht bearbeitet werden. |
|
1101 | 1102 | text_project_destroy_confirmation: Sind Sie sicher, dass Sie das Projekt lΓΆschen wollen? |
|
1102 | 1103 | text_project_identifier_info: 'Kleinbuchstaben (a-z), Ziffern, Binde- und Unterstriche erlaubt, muss mit einem Kleinbuchstaben beginnen.<br />Einmal gespeichert, kann die Kennung nicht mehr geΓ€ndert werden.' |
|
1103 | 1104 | text_reassign_time_entries: 'Gebuchte AufwΓ€nde diesem Ticket zuweisen:' |
|
1104 | 1105 | text_regexp_info: z. B. ^[A-Z0-9]+$ |
|
1105 | 1106 | text_repository_identifier_info: 'Kleinbuchstaben (a-z), Ziffern, Binde- und Unterstriche erlaubt.<br />Einmal gespeichert, kann die Kennung nicht mehr geΓ€ndert werden.' |
|
1106 | 1107 | text_repository_usernames_mapping: "Bitte legen Sie die Zuordnung der Redmine-Benutzer zu den Benutzernamen der Commit-Nachrichten des Projektarchivs fest.\nBenutzer mit identischen Redmine- und Projektarchiv-Benutzernamen oder -E-Mail-Adressen werden automatisch zugeordnet." |
|
1107 | 1108 | text_rmagick_available: RMagick verfΓΌgbar (optional) |
|
1108 | 1109 | text_scm_command: Kommando |
|
1109 | 1110 | text_scm_command_not_available: SCM-Kommando ist nicht verfΓΌgbar. Bitte prΓΌfen Sie die Einstellungen im Administrationspanel. |
|
1110 | 1111 | text_scm_command_version: Version |
|
1111 | 1112 | text_scm_config: Die SCM-Kommandos kΓΆnnen in der in config/configuration.yml konfiguriert werden. Redmine muss anschlieΓend neu gestartet werden. |
|
1112 | 1113 | text_scm_path_encoding_note: "Standard: UTF-8" |
|
1113 | 1114 | text_select_mail_notifications: Bitte wΓ€hlen Sie die Aktionen aus, fΓΌr die eine Mailbenachrichtigung gesendet werden soll. |
|
1114 | 1115 | text_select_project_modules: 'Bitte wΓ€hlen Sie die Module aus, die in diesem Projekt aktiviert sein sollen:' |
|
1115 | 1116 | text_session_expiration_settings: "Achtung: Γnderungen kΓΆnnen aktuelle Sitzungen beenden, Ihre eingeschlossen!" |
|
1116 | 1117 | text_status_changed_by_changeset: "Status geΓ€ndert durch Changeset %{value}." |
|
1117 | 1118 | text_subprojects_destroy_warning: "Dessen Unterprojekte (%{value}) werden ebenfalls gelΓΆscht." |
|
1118 | 1119 | text_subversion_repository_note: 'Beispiele: file:///, http://, https://, svn://, svn+[tunnelscheme]://' |
|
1119 | 1120 | text_time_entries_destroy_confirmation: Sind Sie sicher, dass Sie die ausgewΓ€hlten ZeitaufwΓ€nde lΓΆschen mΓΆchten? |
|
1120 | 1121 | text_time_logged_by_changeset: Angewendet in Changeset %{value}. |
|
1121 | 1122 | text_tip_issue_begin_day: Aufgabe, die an diesem Tag beginnt |
|
1122 | 1123 | text_tip_issue_begin_end_day: Aufgabe, die an diesem Tag beginnt und endet |
|
1123 | 1124 | text_tip_issue_end_day: Aufgabe, die an diesem Tag endet |
|
1124 | 1125 | text_tracker_no_workflow: Kein Workflow fΓΌr diesen Tracker definiert. |
|
1125 | 1126 | text_turning_multiple_off: Wenn Sie die Mehrfachauswahl deaktivieren, werden Felder mit Mehrfachauswahl bereinigt. |
|
1126 | 1127 | Dadurch wird sichergestellt, dass lediglich ein Wert pro Feld ausgewΓ€hlt ist. |
|
1127 | 1128 | text_unallowed_characters: Nicht erlaubte Zeichen |
|
1128 | 1129 | text_user_mail_option: "FΓΌr nicht ausgewΓ€hlte Projekte werden Sie nur Benachrichtigungen fΓΌr Dinge erhalten, die Sie beobachten oder an denen Sie beteiligt sind (z. B. Tickets, deren Autor Sie sind oder die Ihnen zugewiesen sind)." |
|
1129 | 1130 | text_user_wrote: "%{value} schrieb:" |
|
1130 | 1131 | text_warn_on_leaving_unsaved: Die aktuellen Γnderungen gehen verloren, wenn Sie diese Seite verlassen. |
|
1131 | 1132 | text_wiki_destroy_confirmation: Sind Sie sicher, dass Sie dieses Wiki mit sΓ€mtlichem Inhalt lΓΆschen mΓΆchten? |
|
1132 | 1133 | text_wiki_page_destroy_children: LΓΆsche alle Unterseiten |
|
1133 | 1134 | text_wiki_page_destroy_question: "Diese Seite hat %{descendants} Unterseite(n). Was mΓΆchten Sie tun?" |
|
1134 | 1135 | text_wiki_page_nullify_children: Verschiebe die Unterseiten auf die oberste Ebene |
|
1135 | 1136 | text_wiki_page_reassign_children: Ordne die Unterseiten dieser Seite zu |
|
1136 | 1137 | text_workflow_edit: Workflow zum Bearbeiten auswΓ€hlen |
|
1137 | 1138 | text_zoom_in: Ansicht vergrΓΆΓern |
|
1138 | 1139 | text_zoom_out: Ansicht verkleinern |
|
1139 | 1140 | |
|
1140 | 1141 | version_status_closed: abgeschlossen |
|
1141 | 1142 | version_status_locked: gesperrt |
|
1142 | 1143 | version_status_open: offen |
|
1143 | 1144 | |
|
1144 | 1145 | warning_attachments_not_saved: "%{count} Datei(en) konnten nicht gespeichert werden." |
|
1145 | 1146 | label_search_attachments_yes: Namen und Beschreibungen von AnhΓ€ngen durchsuchen |
|
1146 | 1147 | label_search_attachments_no: Keine AnhΓ€nge suchen |
|
1147 | 1148 | label_search_attachments_only: Nur AnhΓ€nge suchen |
|
1148 | 1149 | label_search_open_issues_only: Nur offene Tickets |
|
1149 | 1150 | field_address: E-Mail |
|
1150 | 1151 | setting_max_additional_emails: Maximale Anzahl zusΓ€tzlicher E-Mailadressen |
|
1151 | 1152 | label_email_address_plural: E-Mails |
|
1152 | 1153 | label_email_address_add: E-Mailadresse hinzufΓΌgen |
|
1153 | 1154 | label_enable_notifications: Benachrichtigungen aktivieren |
|
1154 | 1155 | label_disable_notifications: Benachrichtigungen deaktivieren |
|
1155 | 1156 | setting_search_results_per_page: Suchergebnisse pro Seite |
|
1156 | 1157 | label_blank_value: leer |
|
1157 | 1158 | permission_copy_issues: Tickets kopieren |
|
1158 | 1159 | error_password_expired: Ihr Passwort ist abgelaufen oder der Administrator verlangt eine PasswortΓ€nderung. |
|
1159 | 1160 | field_time_entries_visibility: Zeiten-Sichtbarkeit |
|
1160 | 1161 | field_remote_ip: IP-Adresse |
|
1161 | 1162 | label_parent_task_attributes: Eigenschaften ΓΌbergeordneter Aufgaben |
|
1162 | 1163 | label_parent_task_attributes_derived: Abgeleitet von Unteraufgaben |
|
1163 | 1164 | label_parent_task_attributes_independent: UnabhΓ€ngig von Unteraufgaben |
|
1164 | 1165 | label_time_entries_visibility_all: Alle ZeitaufwΓ€nde |
|
1165 | 1166 | label_time_entries_visibility_own: Nur eigene AufwΓ€nde |
|
1166 | 1167 | label_member_management: Mitglieder verwalten |
|
1167 | 1168 | label_member_management_all_roles: Alle Rollen |
|
1168 | 1169 | label_member_management_selected_roles_only: Nur diese Rollen |
|
1169 | 1170 | label_total_spent_time: Aufgewendete Zeit aller Projekte anzeigen |
|
1170 | 1171 | notice_import_finished: "%{count} EintrΓ€ge wurden importiert" |
|
1171 | 1172 | notice_import_finished_with_errors: "%{count} von %{total} EintrΓ€gen konnten nicht importiert werden" |
|
1172 | 1173 | error_invalid_file_encoding: Die Datei ist keine gΓΌltige %{encoding} kodierte Datei |
|
1173 | 1174 | error_invalid_csv_file_or_settings: Die Datei ist keine CSV-Datei oder entspricht nicht den Einstellungen unten |
|
1174 | 1175 | error_can_not_read_import_file: Beim Einlesen der Datei ist ein Fehler aufgetreten |
|
1175 | 1176 | permission_import_issues: Tickets importieren |
|
1176 | 1177 | label_import_issues: Tickets importieren |
|
1177 | 1178 | label_select_file_to_import: Bitte wΓ€hlen Sie eine Datei fΓΌr den Import aus |
|
1178 | 1179 | label_fields_separator: Trennzeichen |
|
1179 | 1180 | label_fields_wrapper: Textqualifizierer |
|
1180 | 1181 | label_encoding: Kodierung |
|
1181 | 1182 | label_comma_char: Komma |
|
1182 | 1183 | label_semi_colon_char: Semikolon |
|
1183 | 1184 | label_quote_char: AnfΓΌhrungszeichen |
|
1184 | 1185 | label_double_quote_char: Doppelte AnfΓΌhrungszeichen |
|
1185 | 1186 | label_fields_mapping: Zuordnung der Felder |
|
1186 | 1187 | label_file_content_preview: Inhaltsvorschau |
|
1187 | 1188 | label_create_missing_values: ErgΓ€nze fehlende Werte |
|
1188 | 1189 | button_import: Importieren |
|
1189 | 1190 | field_total_estimated_hours: Summe des geschΓ€tzten Aufwands |
|
1190 | 1191 | label_api: API |
|
1191 | 1192 | label_total_plural: Summe |
|
1192 | 1193 | label_assigned_issues: Zugewiesene Tickets |
|
1193 | 1194 | label_field_format_enumeration: Eigenschaft/Wert-Paare |
|
1194 | 1195 | label_f_hour_short: '%{value} h' |
|
1195 | 1196 | field_default_version: Standard-Version |
|
1196 | 1197 | error_attachment_extension_not_allowed: Der Dateityp %{extension} des Anhangs ist nicht zugelassen |
|
1197 | 1198 | setting_attachment_extensions_allowed: Zugelassene Dateitypen |
|
1198 | 1199 | setting_attachment_extensions_denied: Nicht zugelassene Dateitypen |
|
1199 | 1200 | label_any_open_issues: irgendein offenes Ticket |
|
1200 | 1201 | label_no_open_issues: kein offenes Ticket |
|
1201 | 1202 | label_default_values_for_new_users: Standardwerte fΓΌr neue Benutzer |
|
1202 | 1203 | error_ldap_bind_credentials: UngΓΌltiges LDAP Konto/Passwort |
|
1203 | 1204 | mail_body_settings_updated: ! 'Die folgenden Einstellungen wurden geΓ€ndert:' |
|
1204 | 1205 | label_relations: Beziehungen |
|
1205 | 1206 | button_filter: Filter |
|
1206 | 1207 | mail_body_password_updated: Ihr Passwort wurde geΓ€ndert. |
|
1207 | 1208 | error_no_tracker_allowed_for_new_issue_in_project: FΓΌr dieses Projekt wurden keine Tracker aktiviert. |
|
1208 | 1209 | label_tracker_all: Alle Tracker |
|
1209 | 1210 | label_new_project_issue_tab_enabled: Tab "Neues Ticket" anzeigen |
|
1210 | 1211 | setting_new_item_menu_tab: MenΓΌ zum Anlegen neuer Objekte |
|
1211 | 1212 | label_new_object_tab_enabled: Dropdown-MenΓΌ "+" anzeigen |
|
1212 | 1213 | error_no_projects_with_tracker_allowed_for_new_issue: There are no projects with trackers |
|
1213 | 1214 | for which you can create an issue |
|
1214 | 1215 | field_textarea_font: Font used for text areas |
|
1215 | 1216 | label_font_default: Default font |
|
1216 | 1217 | label_font_monospace: Monospaced font |
|
1217 | 1218 | label_font_proportional: Proportional font |
@@ -1,1198 +1,1199 | |||
|
1 | 1 | en: |
|
2 | 2 | # Text direction: Left-to-Right (ltr) or Right-to-Left (rtl) |
|
3 | 3 | direction: ltr |
|
4 | 4 | date: |
|
5 | 5 | formats: |
|
6 | 6 | # Use the strftime parameters for formats. |
|
7 | 7 | # When no format has been given, it uses default. |
|
8 | 8 | # You can provide other formats here if you like! |
|
9 | 9 | default: "%m/%d/%Y" |
|
10 | 10 | short: "%b %d" |
|
11 | 11 | long: "%B %d, %Y" |
|
12 | 12 | |
|
13 | 13 | day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday] |
|
14 | 14 | abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat] |
|
15 | 15 | |
|
16 | 16 | # Don't forget the nil at the beginning; there's no such thing as a 0th month |
|
17 | 17 | month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December] |
|
18 | 18 | abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec] |
|
19 | 19 | # Used in date_select and datime_select. |
|
20 | 20 | order: |
|
21 | 21 | - :year |
|
22 | 22 | - :month |
|
23 | 23 | - :day |
|
24 | 24 | |
|
25 | 25 | time: |
|
26 | 26 | formats: |
|
27 | 27 | default: "%m/%d/%Y %I:%M %p" |
|
28 | 28 | time: "%I:%M %p" |
|
29 | 29 | short: "%d %b %H:%M" |
|
30 | 30 | long: "%B %d, %Y %H:%M" |
|
31 | 31 | am: "am" |
|
32 | 32 | pm: "pm" |
|
33 | 33 | |
|
34 | 34 | datetime: |
|
35 | 35 | distance_in_words: |
|
36 | 36 | half_a_minute: "half a minute" |
|
37 | 37 | less_than_x_seconds: |
|
38 | 38 | one: "less than 1 second" |
|
39 | 39 | other: "less than %{count} seconds" |
|
40 | 40 | x_seconds: |
|
41 | 41 | one: "1 second" |
|
42 | 42 | other: "%{count} seconds" |
|
43 | 43 | less_than_x_minutes: |
|
44 | 44 | one: "less than a minute" |
|
45 | 45 | other: "less than %{count} minutes" |
|
46 | 46 | x_minutes: |
|
47 | 47 | one: "1 minute" |
|
48 | 48 | other: "%{count} minutes" |
|
49 | 49 | about_x_hours: |
|
50 | 50 | one: "about 1 hour" |
|
51 | 51 | other: "about %{count} hours" |
|
52 | 52 | x_hours: |
|
53 | 53 | one: "1 hour" |
|
54 | 54 | other: "%{count} hours" |
|
55 | 55 | x_days: |
|
56 | 56 | one: "1 day" |
|
57 | 57 | other: "%{count} days" |
|
58 | 58 | about_x_months: |
|
59 | 59 | one: "about 1 month" |
|
60 | 60 | other: "about %{count} months" |
|
61 | 61 | x_months: |
|
62 | 62 | one: "1 month" |
|
63 | 63 | other: "%{count} months" |
|
64 | 64 | about_x_years: |
|
65 | 65 | one: "about 1 year" |
|
66 | 66 | other: "about %{count} years" |
|
67 | 67 | over_x_years: |
|
68 | 68 | one: "over 1 year" |
|
69 | 69 | other: "over %{count} years" |
|
70 | 70 | almost_x_years: |
|
71 | 71 | one: "almost 1 year" |
|
72 | 72 | other: "almost %{count} years" |
|
73 | 73 | |
|
74 | 74 | number: |
|
75 | 75 | format: |
|
76 | 76 | separator: "." |
|
77 | 77 | delimiter: "" |
|
78 | 78 | precision: 3 |
|
79 | 79 | |
|
80 | 80 | human: |
|
81 | 81 | format: |
|
82 | 82 | delimiter: "" |
|
83 | 83 | precision: 3 |
|
84 | 84 | storage_units: |
|
85 | 85 | format: "%n %u" |
|
86 | 86 | units: |
|
87 | 87 | byte: |
|
88 | 88 | one: "Byte" |
|
89 | 89 | other: "Bytes" |
|
90 | 90 | kb: "KB" |
|
91 | 91 | mb: "MB" |
|
92 | 92 | gb: "GB" |
|
93 | 93 | tb: "TB" |
|
94 | 94 | |
|
95 | 95 | # Used in array.to_sentence. |
|
96 | 96 | support: |
|
97 | 97 | array: |
|
98 | 98 | sentence_connector: "and" |
|
99 | 99 | skip_last_comma: false |
|
100 | 100 | |
|
101 | 101 | activerecord: |
|
102 | 102 | errors: |
|
103 | 103 | template: |
|
104 | 104 | header: |
|
105 | 105 | one: "1 error prohibited this %{model} from being saved" |
|
106 | 106 | other: "%{count} errors prohibited this %{model} from being saved" |
|
107 | 107 | messages: |
|
108 | 108 | inclusion: "is not included in the list" |
|
109 | 109 | exclusion: "is reserved" |
|
110 | 110 | invalid: "is invalid" |
|
111 | 111 | confirmation: "doesn't match confirmation" |
|
112 | 112 | accepted: "must be accepted" |
|
113 | 113 | empty: "cannot be empty" |
|
114 | 114 | blank: "cannot be blank" |
|
115 | 115 | too_long: "is too long (maximum is %{count} characters)" |
|
116 | 116 | too_short: "is too short (minimum is %{count} characters)" |
|
117 | 117 | wrong_length: "is the wrong length (should be %{count} characters)" |
|
118 | 118 | taken: "has already been taken" |
|
119 | 119 | not_a_number: "is not a number" |
|
120 | 120 | not_a_date: "is not a valid date" |
|
121 | 121 | greater_than: "must be greater than %{count}" |
|
122 | 122 | greater_than_or_equal_to: "must be greater than or equal to %{count}" |
|
123 | 123 | equal_to: "must be equal to %{count}" |
|
124 | 124 | less_than: "must be less than %{count}" |
|
125 | 125 | less_than_or_equal_to: "must be less than or equal to %{count}" |
|
126 | 126 | odd: "must be odd" |
|
127 | 127 | even: "must be even" |
|
128 | 128 | greater_than_start_date: "must be greater than start date" |
|
129 | 129 | not_same_project: "doesn't belong to the same project" |
|
130 | 130 | circular_dependency: "This relation would create a circular dependency" |
|
131 | 131 | cant_link_an_issue_with_a_descendant: "An issue cannot be linked to one of its subtasks" |
|
132 | 132 | earlier_than_minimum_start_date: "cannot be earlier than %{date} because of preceding issues" |
|
133 | 133 | |
|
134 | 134 | actionview_instancetag_blank_option: Please select |
|
135 | 135 | |
|
136 | 136 | general_text_No: 'No' |
|
137 | 137 | general_text_Yes: 'Yes' |
|
138 | 138 | general_text_no: 'no' |
|
139 | 139 | general_text_yes: 'yes' |
|
140 | 140 | general_lang_name: 'English' |
|
141 | 141 | general_csv_separator: ',' |
|
142 | 142 | general_csv_decimal_separator: '.' |
|
143 | 143 | general_csv_encoding: ISO-8859-1 |
|
144 | 144 | general_pdf_fontname: freesans |
|
145 | 145 | general_pdf_monospaced_fontname: freemono |
|
146 | 146 | general_first_day_of_week: '7' |
|
147 | 147 | |
|
148 | 148 | notice_account_updated: Account was successfully updated. |
|
149 | 149 | notice_account_invalid_credentials: Invalid user or password |
|
150 | 150 | notice_account_password_updated: Password was successfully updated. |
|
151 | 151 | notice_account_wrong_password: Wrong password |
|
152 | 152 | notice_account_register_done: Account was successfully created. An email containing the instructions to activate your account was sent to %{email}. |
|
153 | 153 | notice_account_unknown_email: Unknown user. |
|
154 | 154 | notice_account_not_activated_yet: You haven't activated your account yet. If you want to receive a new activation email, please <a href="%{url}">click this link</a>. |
|
155 | 155 | notice_account_locked: Your account is locked. |
|
156 | 156 | notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password. |
|
157 | 157 | notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you. |
|
158 | 158 | notice_account_activated: Your account has been activated. You can now log in. |
|
159 | 159 | notice_successful_create: Successful creation. |
|
160 | 160 | notice_successful_update: Successful update. |
|
161 | 161 | notice_successful_delete: Successful deletion. |
|
162 | 162 | notice_successful_connection: Successful connection. |
|
163 | 163 | notice_file_not_found: The page you were trying to access doesn't exist or has been removed. |
|
164 | 164 | notice_locking_conflict: Data has been updated by another user. |
|
165 | 165 | notice_not_authorized: You are not authorized to access this page. |
|
166 | 166 | notice_not_authorized_archived_project: The project you're trying to access has been archived. |
|
167 | 167 | notice_email_sent: "An email was sent to %{value}" |
|
168 | 168 | notice_email_error: "An error occurred while sending mail (%{value})" |
|
169 | 169 | notice_feeds_access_key_reseted: Your Atom access key was reset. |
|
170 | 170 | notice_api_access_key_reseted: Your API access key was reset. |
|
171 | 171 | notice_failed_to_save_issues: "Failed to save %{count} issue(s) on %{total} selected: %{ids}." |
|
172 | 172 | notice_failed_to_save_time_entries: "Failed to save %{count} time entrie(s) on %{total} selected: %{ids}." |
|
173 | 173 | notice_failed_to_save_members: "Failed to save member(s): %{errors}." |
|
174 | 174 | notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit." |
|
175 | 175 | notice_account_pending: "Your account was created and is now pending administrator approval." |
|
176 | 176 | notice_default_data_loaded: Default configuration successfully loaded. |
|
177 | 177 | notice_unable_delete_version: Unable to delete version. |
|
178 | 178 | notice_unable_delete_time_entry: Unable to delete time log entry. |
|
179 | 179 | notice_issue_done_ratios_updated: Issue done ratios updated. |
|
180 | 180 | notice_gantt_chart_truncated: "The chart was truncated because it exceeds the maximum number of items that can be displayed (%{max})" |
|
181 | 181 | notice_issue_successful_create: "Issue %{id} created." |
|
182 | 182 | notice_issue_update_conflict: "The issue has been updated by an other user while you were editing it." |
|
183 | 183 | notice_account_deleted: "Your account has been permanently deleted." |
|
184 | 184 | notice_user_successful_create: "User %{id} created." |
|
185 | 185 | notice_new_password_must_be_different: The new password must be different from the current password |
|
186 | 186 | notice_import_finished: "%{count} items have been imported" |
|
187 | 187 | notice_import_finished_with_errors: "%{count} out of %{total} items could not be imported" |
|
188 | 188 | |
|
189 | 189 | error_can_t_load_default_data: "Default configuration could not be loaded: %{value}" |
|
190 | 190 | error_scm_not_found: "The entry or revision was not found in the repository." |
|
191 | 191 | error_scm_command_failed: "An error occurred when trying to access the repository: %{value}" |
|
192 | 192 | error_scm_annotate: "The entry does not exist or cannot be annotated." |
|
193 | 193 | error_scm_annotate_big_text_file: "The entry cannot be annotated, as it exceeds the maximum text file size." |
|
194 | 194 | error_issue_not_found_in_project: 'The issue was not found or does not belong to this project' |
|
195 | 195 | error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.' |
|
196 | 196 | error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").' |
|
197 | 197 | error_can_not_delete_custom_field: Unable to delete custom field |
|
198 | 198 | error_can_not_delete_tracker: "This tracker contains issues and cannot be deleted." |
|
199 | 199 | error_can_not_remove_role: "This role is in use and cannot be deleted." |
|
200 | 200 | error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version cannot be reopened' |
|
201 | 201 | error_can_not_archive_project: This project cannot be archived |
|
202 | 202 | error_issue_done_ratios_not_updated: "Issue done ratios not updated." |
|
203 | 203 | error_workflow_copy_source: 'Please select a source tracker or role' |
|
204 | 204 | error_workflow_copy_target: 'Please select target tracker(s) and role(s)' |
|
205 | 205 | error_unable_delete_issue_status: 'Unable to delete issue status' |
|
206 | 206 | error_unable_to_connect: "Unable to connect (%{value})" |
|
207 | 207 | error_attachment_too_big: "This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size})" |
|
208 | 208 | error_session_expired: "Your session has expired. Please login again." |
|
209 | 209 | warning_attachments_not_saved: "%{count} file(s) could not be saved." |
|
210 | 210 | error_password_expired: "Your password has expired or the administrator requires you to change it." |
|
211 | 211 | error_invalid_file_encoding: "The file is not a valid %{encoding} encoded file" |
|
212 | 212 | error_invalid_csv_file_or_settings: "The file is not a CSV file or does not match the settings below" |
|
213 | 213 | error_can_not_read_import_file: "An error occurred while reading the file to import" |
|
214 | 214 | error_attachment_extension_not_allowed: "Attachment extension %{extension} is not allowed" |
|
215 | 215 | error_ldap_bind_credentials: "Invalid LDAP Account/Password" |
|
216 | 216 | error_no_tracker_allowed_for_new_issue_in_project: "The project doesn't have any trackers for which you can create an issue" |
|
217 | 217 | error_no_projects_with_tracker_allowed_for_new_issue: "There are no projects with trackers for which you can create an issue" |
|
218 | 218 | |
|
219 | 219 | mail_subject_lost_password: "Your %{value} password" |
|
220 | 220 | mail_body_lost_password: 'To change your password, click on the following link:' |
|
221 | 221 | mail_subject_register: "Your %{value} account activation" |
|
222 | 222 | mail_body_register: 'To activate your account, click on the following link:' |
|
223 | 223 | mail_body_account_information_external: "You can use your %{value} account to log in." |
|
224 | 224 | mail_body_account_information: Your account information |
|
225 | 225 | mail_subject_account_activation_request: "%{value} account activation request" |
|
226 | 226 | mail_body_account_activation_request: "A new user (%{value}) has registered. The account is pending your approval:" |
|
227 | 227 | mail_subject_reminder: "%{count} issue(s) due in the next %{days} days" |
|
228 | 228 | mail_body_reminder: "%{count} issue(s) that are assigned to you are due in the next %{days} days:" |
|
229 | 229 | mail_subject_wiki_content_added: "'%{id}' wiki page has been added" |
|
230 | 230 | mail_body_wiki_content_added: "The '%{id}' wiki page has been added by %{author}." |
|
231 | 231 | mail_subject_wiki_content_updated: "'%{id}' wiki page has been updated" |
|
232 | 232 | mail_body_wiki_content_updated: "The '%{id}' wiki page has been updated by %{author}." |
|
233 | 233 | mail_subject_security_notification: "Security notification" |
|
234 | 234 | mail_body_security_notification_change: "%{field} was changed." |
|
235 | 235 | mail_body_security_notification_change_to: "%{field} was changed to %{value}." |
|
236 | 236 | mail_body_security_notification_add: "%{field} %{value} was added." |
|
237 | 237 | mail_body_security_notification_remove: "%{field} %{value} was removed." |
|
238 | 238 | mail_body_security_notification_notify_enabled: "Email address %{value} now receives notifications." |
|
239 | 239 | mail_body_security_notification_notify_disabled: "Email address %{value} no longer receives notifications." |
|
240 | 240 | mail_body_settings_updated: "The following settings were changed:" |
|
241 | 241 | mail_body_password_updated: "Your password has been changed." |
|
242 | 242 | |
|
243 | 243 | field_name: Name |
|
244 | 244 | field_description: Description |
|
245 | 245 | field_summary: Summary |
|
246 | 246 | field_is_required: Required |
|
247 | 247 | field_firstname: First name |
|
248 | 248 | field_lastname: Last name |
|
249 | 249 | field_mail: Email |
|
250 | 250 | field_address: Email |
|
251 | 251 | field_filename: File |
|
252 | 252 | field_filesize: Size |
|
253 | 253 | field_downloads: Downloads |
|
254 | 254 | field_author: Author |
|
255 | 255 | field_created_on: Created |
|
256 | 256 | field_updated_on: Updated |
|
257 | 257 | field_closed_on: Closed |
|
258 | 258 | field_field_format: Format |
|
259 | 259 | field_is_for_all: For all projects |
|
260 | 260 | field_possible_values: Possible values |
|
261 | 261 | field_regexp: Regular expression |
|
262 | 262 | field_min_length: Minimum length |
|
263 | 263 | field_max_length: Maximum length |
|
264 | 264 | field_value: Value |
|
265 | 265 | field_category: Category |
|
266 | 266 | field_title: Title |
|
267 | 267 | field_project: Project |
|
268 | 268 | field_issue: Issue |
|
269 | 269 | field_status: Status |
|
270 | 270 | field_notes: Notes |
|
271 | 271 | field_is_closed: Issue closed |
|
272 | 272 | field_is_default: Default value |
|
273 | 273 | field_tracker: Tracker |
|
274 | 274 | field_subject: Subject |
|
275 | 275 | field_due_date: Due date |
|
276 | 276 | field_assigned_to: Assignee |
|
277 | 277 | field_priority: Priority |
|
278 | 278 | field_fixed_version: Target version |
|
279 | 279 | field_user: User |
|
280 | 280 | field_principal: Principal |
|
281 | 281 | field_role: Role |
|
282 | 282 | field_homepage: Homepage |
|
283 | 283 | field_is_public: Public |
|
284 | 284 | field_parent: Subproject of |
|
285 | 285 | field_is_in_roadmap: Issues displayed in roadmap |
|
286 | 286 | field_login: Login |
|
287 | 287 | field_mail_notification: Email notifications |
|
288 | 288 | field_admin: Administrator |
|
289 | 289 | field_last_login_on: Last connection |
|
290 | 290 | field_language: Language |
|
291 | 291 | field_effective_date: Due date |
|
292 | 292 | field_password: Password |
|
293 | 293 | field_new_password: New password |
|
294 | 294 | field_password_confirmation: Confirmation |
|
295 | 295 | field_version: Version |
|
296 | 296 | field_type: Type |
|
297 | 297 | field_host: Host |
|
298 | 298 | field_port: Port |
|
299 | 299 | field_account: Account |
|
300 | 300 | field_base_dn: Base DN |
|
301 | 301 | field_attr_login: Login attribute |
|
302 | 302 | field_attr_firstname: Firstname attribute |
|
303 | 303 | field_attr_lastname: Lastname attribute |
|
304 | 304 | field_attr_mail: Email attribute |
|
305 | 305 | field_onthefly: On-the-fly user creation |
|
306 | 306 | field_start_date: Start date |
|
307 | 307 | field_done_ratio: "% Done" |
|
308 | 308 | field_auth_source: Authentication mode |
|
309 | 309 | field_hide_mail: Hide my email address |
|
310 | 310 | field_comments: Comment |
|
311 | 311 | field_url: URL |
|
312 | 312 | field_start_page: Start page |
|
313 | 313 | field_subproject: Subproject |
|
314 | 314 | field_hours: Hours |
|
315 | 315 | field_activity: Activity |
|
316 | 316 | field_spent_on: Date |
|
317 | 317 | field_identifier: Identifier |
|
318 | 318 | field_is_filter: Used as a filter |
|
319 | 319 | field_issue_to: Related issue |
|
320 | 320 | field_delay: Delay |
|
321 | 321 | field_assignable: Issues can be assigned to this role |
|
322 | 322 | field_redirect_existing_links: Redirect existing links |
|
323 | 323 | field_estimated_hours: Estimated time |
|
324 | 324 | field_column_names: Columns |
|
325 | 325 | field_time_entries: Log time |
|
326 | 326 | field_time_zone: Time zone |
|
327 | 327 | field_searchable: Searchable |
|
328 | 328 | field_default_value: Default value |
|
329 | 329 | field_comments_sorting: Display comments |
|
330 | 330 | field_parent_title: Parent page |
|
331 | 331 | field_editable: Editable |
|
332 | 332 | field_watcher: Watcher |
|
333 | 333 | field_identity_url: OpenID URL |
|
334 | 334 | field_content: Content |
|
335 | 335 | field_group_by: Group results by |
|
336 | 336 | field_sharing: Sharing |
|
337 | 337 | field_parent_issue: Parent task |
|
338 | 338 | field_member_of_group: "Assignee's group" |
|
339 | 339 | field_assigned_to_role: "Assignee's role" |
|
340 | 340 | field_text: Text field |
|
341 | 341 | field_visible: Visible |
|
342 | 342 | field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text" |
|
343 | 343 | field_issues_visibility: Issues visibility |
|
344 | 344 | field_is_private: Private |
|
345 | 345 | field_commit_logs_encoding: Commit messages encoding |
|
346 | 346 | field_scm_path_encoding: Path encoding |
|
347 | 347 | field_path_to_repository: Path to repository |
|
348 | 348 | field_root_directory: Root directory |
|
349 | 349 | field_cvsroot: CVSROOT |
|
350 | 350 | field_cvs_module: Module |
|
351 | 351 | field_repository_is_default: Main repository |
|
352 | 352 | field_multiple: Multiple values |
|
353 | 353 | field_auth_source_ldap_filter: LDAP filter |
|
354 | 354 | field_core_fields: Standard fields |
|
355 | 355 | field_timeout: "Timeout (in seconds)" |
|
356 | 356 | field_board_parent: Parent forum |
|
357 | 357 | field_private_notes: Private notes |
|
358 | 358 | field_inherit_members: Inherit members |
|
359 | 359 | field_generate_password: Generate password |
|
360 | 360 | field_must_change_passwd: Must change password at next logon |
|
361 | 361 | field_default_status: Default status |
|
362 | 362 | field_users_visibility: Users visibility |
|
363 | 363 | field_time_entries_visibility: Time logs visibility |
|
364 | 364 | field_total_estimated_hours: Total estimated time |
|
365 | 365 | field_default_version: Default version |
|
366 | 366 | field_remote_ip: IP address |
|
367 | 367 | field_textarea_font: Font used for text areas |
|
368 | 368 | |
|
369 | 369 | setting_app_title: Application title |
|
370 | 370 | setting_app_subtitle: Application subtitle |
|
371 | 371 | setting_welcome_text: Welcome text |
|
372 | 372 | setting_default_language: Default language |
|
373 | 373 | setting_login_required: Authentication required |
|
374 | 374 | setting_self_registration: Self-registration |
|
375 | 375 | setting_attachment_max_size: Maximum attachment size |
|
376 | 376 | setting_issues_export_limit: Issues export limit |
|
377 | 377 | setting_mail_from: Emission email address |
|
378 | 378 | setting_bcc_recipients: Blind carbon copy recipients (bcc) |
|
379 | 379 | setting_plain_text_mail: Plain text mail (no HTML) |
|
380 | 380 | setting_host_name: Host name and path |
|
381 | 381 | setting_text_formatting: Text formatting |
|
382 | 382 | setting_wiki_compression: Wiki history compression |
|
383 | 383 | setting_feeds_limit: Maximum number of items in Atom feeds |
|
384 | 384 | setting_default_projects_public: New projects are public by default |
|
385 | 385 | setting_autofetch_changesets: Fetch commits automatically |
|
386 | 386 | setting_sys_api_enabled: Enable WS for repository management |
|
387 | 387 | setting_commit_ref_keywords: Referencing keywords |
|
388 | 388 | setting_commit_fix_keywords: Fixing keywords |
|
389 | 389 | setting_autologin: Autologin |
|
390 | 390 | setting_date_format: Date format |
|
391 | 391 | setting_time_format: Time format |
|
392 | setting_timespan_format: Time span format | |
|
392 | 393 | setting_cross_project_issue_relations: Allow cross-project issue relations |
|
393 | 394 | setting_cross_project_subtasks: Allow cross-project subtasks |
|
394 | 395 | setting_issue_list_default_columns: Default columns displayed on the issue list |
|
395 | 396 | setting_repositories_encodings: Attachments and repositories encodings |
|
396 | 397 | setting_emails_header: Email header |
|
397 | 398 | setting_emails_footer: Email footer |
|
398 | 399 | setting_protocol: Protocol |
|
399 | 400 | setting_per_page_options: Objects per page options |
|
400 | 401 | setting_user_format: Users display format |
|
401 | 402 | setting_activity_days_default: Days displayed on project activity |
|
402 | 403 | setting_display_subprojects_issues: Display subprojects issues on main projects by default |
|
403 | 404 | setting_enabled_scm: Enabled SCM |
|
404 | 405 | setting_mail_handler_body_delimiters: "Truncate emails after one of these lines" |
|
405 | 406 | setting_mail_handler_api_enabled: Enable WS for incoming emails |
|
406 | 407 | setting_mail_handler_api_key: Incoming email WS API key |
|
407 | 408 | setting_sys_api_key: Repository management WS API key |
|
408 | 409 | setting_sequential_project_identifiers: Generate sequential project identifiers |
|
409 | 410 | setting_gravatar_enabled: Use Gravatar user icons |
|
410 | 411 | setting_gravatar_default: Default Gravatar image |
|
411 | 412 | setting_diff_max_lines_displayed: Maximum number of diff lines displayed |
|
412 | 413 | setting_file_max_size_displayed: Maximum size of text files displayed inline |
|
413 | 414 | setting_repository_log_display_limit: Maximum number of revisions displayed on file log |
|
414 | 415 | setting_openid: Allow OpenID login and registration |
|
415 | 416 | setting_password_max_age: Require password change after |
|
416 | 417 | setting_password_min_length: Minimum password length |
|
417 | 418 | setting_lost_password: Allow password reset via email |
|
418 | 419 | setting_new_project_user_role_id: Role given to a non-admin user who creates a project |
|
419 | 420 | setting_default_projects_modules: Default enabled modules for new projects |
|
420 | 421 | setting_issue_done_ratio: Calculate the issue done ratio with |
|
421 | 422 | setting_issue_done_ratio_issue_field: Use the issue field |
|
422 | 423 | setting_issue_done_ratio_issue_status: Use the issue status |
|
423 | 424 | setting_start_of_week: Start calendars on |
|
424 | 425 | setting_rest_api_enabled: Enable REST web service |
|
425 | 426 | setting_cache_formatted_text: Cache formatted text |
|
426 | 427 | setting_default_notification_option: Default notification option |
|
427 | 428 | setting_commit_logtime_enabled: Enable time logging |
|
428 | 429 | setting_commit_logtime_activity_id: Activity for logged time |
|
429 | 430 | setting_gantt_items_limit: Maximum number of items displayed on the gantt chart |
|
430 | 431 | setting_issue_group_assignment: Allow issue assignment to groups |
|
431 | 432 | setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues |
|
432 | 433 | setting_commit_cross_project_ref: Allow issues of all the other projects to be referenced and fixed |
|
433 | 434 | setting_unsubscribe: Allow users to delete their own account |
|
434 | 435 | setting_session_lifetime: Session maximum lifetime |
|
435 | 436 | setting_session_timeout: Session inactivity timeout |
|
436 | 437 | setting_thumbnails_enabled: Display attachment thumbnails |
|
437 | 438 | setting_thumbnails_size: Thumbnails size (in pixels) |
|
438 | 439 | setting_non_working_week_days: Non-working days |
|
439 | 440 | setting_jsonp_enabled: Enable JSONP support |
|
440 | 441 | setting_default_projects_tracker_ids: Default trackers for new projects |
|
441 | 442 | setting_mail_handler_excluded_filenames: Exclude attachments by name |
|
442 | 443 | setting_force_default_language_for_anonymous: Force default language for anonymous users |
|
443 | 444 | setting_force_default_language_for_loggedin: Force default language for logged-in users |
|
444 | 445 | setting_link_copied_issue: Link issues on copy |
|
445 | 446 | setting_max_additional_emails: Maximum number of additional email addresses |
|
446 | 447 | setting_search_results_per_page: Search results per page |
|
447 | 448 | setting_attachment_extensions_allowed: Allowed extensions |
|
448 | 449 | setting_attachment_extensions_denied: Disallowed extensions |
|
449 | 450 | setting_new_item_menu_tab: Project menu tab for creating new objects |
|
450 | 451 | |
|
451 | 452 | permission_add_project: Create project |
|
452 | 453 | permission_add_subprojects: Create subprojects |
|
453 | 454 | permission_edit_project: Edit project |
|
454 | 455 | permission_close_project: Close / reopen the project |
|
455 | 456 | permission_select_project_modules: Select project modules |
|
456 | 457 | permission_manage_members: Manage members |
|
457 | 458 | permission_manage_project_activities: Manage project activities |
|
458 | 459 | permission_manage_versions: Manage versions |
|
459 | 460 | permission_manage_categories: Manage issue categories |
|
460 | 461 | permission_view_issues: View Issues |
|
461 | 462 | permission_add_issues: Add issues |
|
462 | 463 | permission_edit_issues: Edit issues |
|
463 | 464 | permission_copy_issues: Copy issues |
|
464 | 465 | permission_manage_issue_relations: Manage issue relations |
|
465 | 466 | permission_set_issues_private: Set issues public or private |
|
466 | 467 | permission_set_own_issues_private: Set own issues public or private |
|
467 | 468 | permission_add_issue_notes: Add notes |
|
468 | 469 | permission_edit_issue_notes: Edit notes |
|
469 | 470 | permission_edit_own_issue_notes: Edit own notes |
|
470 | 471 | permission_view_private_notes: View private notes |
|
471 | 472 | permission_set_notes_private: Set notes as private |
|
472 | 473 | permission_move_issues: Move issues |
|
473 | 474 | permission_delete_issues: Delete issues |
|
474 | 475 | permission_manage_public_queries: Manage public queries |
|
475 | 476 | permission_save_queries: Save queries |
|
476 | 477 | permission_view_gantt: View gantt chart |
|
477 | 478 | permission_view_calendar: View calendar |
|
478 | 479 | permission_view_issue_watchers: View watchers list |
|
479 | 480 | permission_add_issue_watchers: Add watchers |
|
480 | 481 | permission_delete_issue_watchers: Delete watchers |
|
481 | 482 | permission_log_time: Log spent time |
|
482 | 483 | permission_view_time_entries: View spent time |
|
483 | 484 | permission_edit_time_entries: Edit time logs |
|
484 | 485 | permission_edit_own_time_entries: Edit own time logs |
|
485 | 486 | permission_manage_news: Manage news |
|
486 | 487 | permission_comment_news: Comment news |
|
487 | 488 | permission_view_documents: View documents |
|
488 | 489 | permission_add_documents: Add documents |
|
489 | 490 | permission_edit_documents: Edit documents |
|
490 | 491 | permission_delete_documents: Delete documents |
|
491 | 492 | permission_manage_files: Manage files |
|
492 | 493 | permission_view_files: View files |
|
493 | 494 | permission_manage_wiki: Manage wiki |
|
494 | 495 | permission_rename_wiki_pages: Rename wiki pages |
|
495 | 496 | permission_delete_wiki_pages: Delete wiki pages |
|
496 | 497 | permission_view_wiki_pages: View wiki |
|
497 | 498 | permission_view_wiki_edits: View wiki history |
|
498 | 499 | permission_edit_wiki_pages: Edit wiki pages |
|
499 | 500 | permission_delete_wiki_pages_attachments: Delete attachments |
|
500 | 501 | permission_protect_wiki_pages: Protect wiki pages |
|
501 | 502 | permission_manage_repository: Manage repository |
|
502 | 503 | permission_browse_repository: Browse repository |
|
503 | 504 | permission_view_changesets: View changesets |
|
504 | 505 | permission_commit_access: Commit access |
|
505 | 506 | permission_manage_boards: Manage forums |
|
506 | 507 | permission_view_messages: View messages |
|
507 | 508 | permission_add_messages: Post messages |
|
508 | 509 | permission_edit_messages: Edit messages |
|
509 | 510 | permission_edit_own_messages: Edit own messages |
|
510 | 511 | permission_delete_messages: Delete messages |
|
511 | 512 | permission_delete_own_messages: Delete own messages |
|
512 | 513 | permission_export_wiki_pages: Export wiki pages |
|
513 | 514 | permission_manage_subtasks: Manage subtasks |
|
514 | 515 | permission_manage_related_issues: Manage related issues |
|
515 | 516 | permission_import_issues: Import issues |
|
516 | 517 | |
|
517 | 518 | project_module_issue_tracking: Issue tracking |
|
518 | 519 | project_module_time_tracking: Time tracking |
|
519 | 520 | project_module_news: News |
|
520 | 521 | project_module_documents: Documents |
|
521 | 522 | project_module_files: Files |
|
522 | 523 | project_module_wiki: Wiki |
|
523 | 524 | project_module_repository: Repository |
|
524 | 525 | project_module_boards: Forums |
|
525 | 526 | project_module_calendar: Calendar |
|
526 | 527 | project_module_gantt: Gantt |
|
527 | 528 | |
|
528 | 529 | label_user: User |
|
529 | 530 | label_user_plural: Users |
|
530 | 531 | label_user_new: New user |
|
531 | 532 | label_user_anonymous: Anonymous |
|
532 | 533 | label_project: Project |
|
533 | 534 | label_project_new: New project |
|
534 | 535 | label_project_plural: Projects |
|
535 | 536 | label_x_projects: |
|
536 | 537 | zero: no projects |
|
537 | 538 | one: 1 project |
|
538 | 539 | other: "%{count} projects" |
|
539 | 540 | label_project_all: All Projects |
|
540 | 541 | label_project_latest: Latest projects |
|
541 | 542 | label_issue: Issue |
|
542 | 543 | label_issue_new: New issue |
|
543 | 544 | label_issue_plural: Issues |
|
544 | 545 | label_issue_view_all: View all issues |
|
545 | 546 | label_issues_by: "Issues by %{value}" |
|
546 | 547 | label_issue_added: Issue added |
|
547 | 548 | label_issue_updated: Issue updated |
|
548 | 549 | label_issue_note_added: Note added |
|
549 | 550 | label_issue_status_updated: Status updated |
|
550 | 551 | label_issue_assigned_to_updated: Assignee updated |
|
551 | 552 | label_issue_priority_updated: Priority updated |
|
552 | 553 | label_document: Document |
|
553 | 554 | label_document_new: New document |
|
554 | 555 | label_document_plural: Documents |
|
555 | 556 | label_document_added: Document added |
|
556 | 557 | label_role: Role |
|
557 | 558 | label_role_plural: Roles |
|
558 | 559 | label_role_new: New role |
|
559 | 560 | label_role_and_permissions: Roles and permissions |
|
560 | 561 | label_role_anonymous: Anonymous |
|
561 | 562 | label_role_non_member: Non member |
|
562 | 563 | label_member: Member |
|
563 | 564 | label_member_new: New member |
|
564 | 565 | label_member_plural: Members |
|
565 | 566 | label_tracker: Tracker |
|
566 | 567 | label_tracker_plural: Trackers |
|
567 | 568 | label_tracker_all: All trackers |
|
568 | 569 | label_tracker_new: New tracker |
|
569 | 570 | label_workflow: Workflow |
|
570 | 571 | label_issue_status: Issue status |
|
571 | 572 | label_issue_status_plural: Issue statuses |
|
572 | 573 | label_issue_status_new: New status |
|
573 | 574 | label_issue_category: Issue category |
|
574 | 575 | label_issue_category_plural: Issue categories |
|
575 | 576 | label_issue_category_new: New category |
|
576 | 577 | label_custom_field: Custom field |
|
577 | 578 | label_custom_field_plural: Custom fields |
|
578 | 579 | label_custom_field_new: New custom field |
|
579 | 580 | label_enumerations: Enumerations |
|
580 | 581 | label_enumeration_new: New value |
|
581 | 582 | label_information: Information |
|
582 | 583 | label_information_plural: Information |
|
583 | 584 | label_please_login: Please log in |
|
584 | 585 | label_register: Register |
|
585 | 586 | label_login_with_open_id_option: or login with OpenID |
|
586 | 587 | label_password_lost: Lost password |
|
587 | 588 | label_password_required: Confirm your password to continue |
|
588 | 589 | label_home: Home |
|
589 | 590 | label_my_page: My page |
|
590 | 591 | label_my_account: My account |
|
591 | 592 | label_my_projects: My projects |
|
592 | 593 | label_my_page_block: My page block |
|
593 | 594 | label_administration: Administration |
|
594 | 595 | label_login: Sign in |
|
595 | 596 | label_logout: Sign out |
|
596 | 597 | label_help: Help |
|
597 | 598 | label_reported_issues: Reported issues |
|
598 | 599 | label_assigned_issues: Assigned issues |
|
599 | 600 | label_assigned_to_me_issues: Issues assigned to me |
|
600 | 601 | label_last_login: Last connection |
|
601 | 602 | label_registered_on: Registered on |
|
602 | 603 | label_activity: Activity |
|
603 | 604 | label_overall_activity: Overall activity |
|
604 | 605 | label_user_activity: "%{value}'s activity" |
|
605 | 606 | label_new: New |
|
606 | 607 | label_logged_as: Logged in as |
|
607 | 608 | label_environment: Environment |
|
608 | 609 | label_authentication: Authentication |
|
609 | 610 | label_auth_source: Authentication mode |
|
610 | 611 | label_auth_source_new: New authentication mode |
|
611 | 612 | label_auth_source_plural: Authentication modes |
|
612 | 613 | label_subproject_plural: Subprojects |
|
613 | 614 | label_subproject_new: New subproject |
|
614 | 615 | label_and_its_subprojects: "%{value} and its subprojects" |
|
615 | 616 | label_min_max_length: Min - Max length |
|
616 | 617 | label_list: List |
|
617 | 618 | label_date: Date |
|
618 | 619 | label_integer: Integer |
|
619 | 620 | label_float: Float |
|
620 | 621 | label_boolean: Boolean |
|
621 | 622 | label_string: Text |
|
622 | 623 | label_text: Long text |
|
623 | 624 | label_attribute: Attribute |
|
624 | 625 | label_attribute_plural: Attributes |
|
625 | 626 | label_no_data: No data to display |
|
626 | 627 | label_no_preview: No preview available |
|
627 | 628 | label_change_status: Change status |
|
628 | 629 | label_history: History |
|
629 | 630 | label_attachment: File |
|
630 | 631 | label_attachment_new: New file |
|
631 | 632 | label_attachment_delete: Delete file |
|
632 | 633 | label_attachment_plural: Files |
|
633 | 634 | label_file_added: File added |
|
634 | 635 | label_report: Report |
|
635 | 636 | label_report_plural: Reports |
|
636 | 637 | label_news: News |
|
637 | 638 | label_news_new: Add news |
|
638 | 639 | label_news_plural: News |
|
639 | 640 | label_news_latest: Latest news |
|
640 | 641 | label_news_view_all: View all news |
|
641 | 642 | label_news_added: News added |
|
642 | 643 | label_news_comment_added: Comment added to a news |
|
643 | 644 | label_settings: Settings |
|
644 | 645 | label_overview: Overview |
|
645 | 646 | label_version: Version |
|
646 | 647 | label_version_new: New version |
|
647 | 648 | label_version_plural: Versions |
|
648 | 649 | label_close_versions: Close completed versions |
|
649 | 650 | label_confirmation: Confirmation |
|
650 | 651 | label_export_to: 'Also available in:' |
|
651 | 652 | label_read: Read... |
|
652 | 653 | label_public_projects: Public projects |
|
653 | 654 | label_open_issues: open |
|
654 | 655 | label_open_issues_plural: open |
|
655 | 656 | label_closed_issues: closed |
|
656 | 657 | label_closed_issues_plural: closed |
|
657 | 658 | label_x_open_issues_abbr: |
|
658 | 659 | zero: 0 open |
|
659 | 660 | one: 1 open |
|
660 | 661 | other: "%{count} open" |
|
661 | 662 | label_x_closed_issues_abbr: |
|
662 | 663 | zero: 0 closed |
|
663 | 664 | one: 1 closed |
|
664 | 665 | other: "%{count} closed" |
|
665 | 666 | label_x_issues: |
|
666 | 667 | zero: 0 issues |
|
667 | 668 | one: 1 issue |
|
668 | 669 | other: "%{count} issues" |
|
669 | 670 | label_total: Total |
|
670 | 671 | label_total_plural: Totals |
|
671 | 672 | label_total_time: Total time |
|
672 | 673 | label_permissions: Permissions |
|
673 | 674 | label_current_status: Current status |
|
674 | 675 | label_new_statuses_allowed: New statuses allowed |
|
675 | 676 | label_all: all |
|
676 | 677 | label_any: any |
|
677 | 678 | label_none: none |
|
678 | 679 | label_nobody: nobody |
|
679 | 680 | label_next: Next |
|
680 | 681 | label_previous: Previous |
|
681 | 682 | label_used_by: Used by |
|
682 | 683 | label_details: Details |
|
683 | 684 | label_add_note: Add a note |
|
684 | 685 | label_calendar: Calendar |
|
685 | 686 | label_months_from: months from |
|
686 | 687 | label_gantt: Gantt |
|
687 | 688 | label_internal: Internal |
|
688 | 689 | label_last_changes: "last %{count} changes" |
|
689 | 690 | label_change_view_all: View all changes |
|
690 | 691 | label_personalize_page: Personalize this page |
|
691 | 692 | label_comment: Comment |
|
692 | 693 | label_comment_plural: Comments |
|
693 | 694 | label_x_comments: |
|
694 | 695 | zero: no comments |
|
695 | 696 | one: 1 comment |
|
696 | 697 | other: "%{count} comments" |
|
697 | 698 | label_comment_add: Add a comment |
|
698 | 699 | label_comment_added: Comment added |
|
699 | 700 | label_comment_delete: Delete comments |
|
700 | 701 | label_query: Custom query |
|
701 | 702 | label_query_plural: Custom queries |
|
702 | 703 | label_query_new: New query |
|
703 | 704 | label_my_queries: My custom queries |
|
704 | 705 | label_filter_add: Add filter |
|
705 | 706 | label_filter_plural: Filters |
|
706 | 707 | label_equals: is |
|
707 | 708 | label_not_equals: is not |
|
708 | 709 | label_in_less_than: in less than |
|
709 | 710 | label_in_more_than: in more than |
|
710 | 711 | label_in_the_next_days: in the next |
|
711 | 712 | label_in_the_past_days: in the past |
|
712 | 713 | label_greater_or_equal: '>=' |
|
713 | 714 | label_less_or_equal: '<=' |
|
714 | 715 | label_between: between |
|
715 | 716 | label_in: in |
|
716 | 717 | label_today: today |
|
717 | 718 | label_all_time: all time |
|
718 | 719 | label_yesterday: yesterday |
|
719 | 720 | label_this_week: this week |
|
720 | 721 | label_last_week: last week |
|
721 | 722 | label_last_n_weeks: "last %{count} weeks" |
|
722 | 723 | label_last_n_days: "last %{count} days" |
|
723 | 724 | label_this_month: this month |
|
724 | 725 | label_last_month: last month |
|
725 | 726 | label_this_year: this year |
|
726 | 727 | label_date_range: Date range |
|
727 | 728 | label_less_than_ago: less than days ago |
|
728 | 729 | label_more_than_ago: more than days ago |
|
729 | 730 | label_ago: days ago |
|
730 | 731 | label_contains: contains |
|
731 | 732 | label_not_contains: doesn't contain |
|
732 | 733 | label_any_issues_in_project: any issues in project |
|
733 | 734 | label_any_issues_not_in_project: any issues not in project |
|
734 | 735 | label_no_issues_in_project: no issues in project |
|
735 | 736 | label_any_open_issues: any open issues |
|
736 | 737 | label_no_open_issues: no open issues |
|
737 | 738 | label_day_plural: days |
|
738 | 739 | label_repository: Repository |
|
739 | 740 | label_repository_new: New repository |
|
740 | 741 | label_repository_plural: Repositories |
|
741 | 742 | label_browse: Browse |
|
742 | 743 | label_branch: Branch |
|
743 | 744 | label_tag: Tag |
|
744 | 745 | label_revision: Revision |
|
745 | 746 | label_revision_plural: Revisions |
|
746 | 747 | label_revision_id: "Revision %{value}" |
|
747 | 748 | label_associated_revisions: Associated revisions |
|
748 | 749 | label_added: added |
|
749 | 750 | label_modified: modified |
|
750 | 751 | label_copied: copied |
|
751 | 752 | label_renamed: renamed |
|
752 | 753 | label_deleted: deleted |
|
753 | 754 | label_latest_revision: Latest revision |
|
754 | 755 | label_latest_revision_plural: Latest revisions |
|
755 | 756 | label_view_revisions: View revisions |
|
756 | 757 | label_view_all_revisions: View all revisions |
|
757 | 758 | label_max_size: Maximum size |
|
758 | 759 | label_sort_highest: Move to top |
|
759 | 760 | label_sort_higher: Move up |
|
760 | 761 | label_sort_lower: Move down |
|
761 | 762 | label_sort_lowest: Move to bottom |
|
762 | 763 | label_roadmap: Roadmap |
|
763 | 764 | label_roadmap_due_in: "Due in %{value}" |
|
764 | 765 | label_roadmap_overdue: "%{value} late" |
|
765 | 766 | label_roadmap_no_issues: No issues for this version |
|
766 | 767 | label_search: Search |
|
767 | 768 | label_result_plural: Results |
|
768 | 769 | label_all_words: All words |
|
769 | 770 | label_wiki: Wiki |
|
770 | 771 | label_wiki_edit: Wiki edit |
|
771 | 772 | label_wiki_edit_plural: Wiki edits |
|
772 | 773 | label_wiki_page: Wiki page |
|
773 | 774 | label_wiki_page_plural: Wiki pages |
|
774 | 775 | label_wiki_page_new: New wiki page |
|
775 | 776 | label_index_by_title: Index by title |
|
776 | 777 | label_index_by_date: Index by date |
|
777 | 778 | label_current_version: Current version |
|
778 | 779 | label_preview: Preview |
|
779 | 780 | label_feed_plural: Feeds |
|
780 | 781 | label_changes_details: Details of all changes |
|
781 | 782 | label_issue_tracking: Issue tracking |
|
782 | 783 | label_spent_time: Spent time |
|
783 | 784 | label_total_spent_time: Total spent time |
|
784 | 785 | label_overall_spent_time: Overall spent time |
|
785 | 786 | label_f_hour: "%{value} hour" |
|
786 | 787 | label_f_hour_plural: "%{value} hours" |
|
787 | 788 | label_f_hour_short: "%{value} h" |
|
788 | 789 | label_time_tracking: Time tracking |
|
789 | 790 | label_change_plural: Changes |
|
790 | 791 | label_statistics: Statistics |
|
791 | 792 | label_commits_per_month: Commits per month |
|
792 | 793 | label_commits_per_author: Commits per author |
|
793 | 794 | label_diff: diff |
|
794 | 795 | label_view_diff: View differences |
|
795 | 796 | label_diff_inline: inline |
|
796 | 797 | label_diff_side_by_side: side by side |
|
797 | 798 | label_options: Options |
|
798 | 799 | label_copy_workflow_from: Copy workflow from |
|
799 | 800 | label_permissions_report: Permissions report |
|
800 | 801 | label_watched_issues: Watched issues |
|
801 | 802 | label_related_issues: Related issues |
|
802 | 803 | label_applied_status: Applied status |
|
803 | 804 | label_loading: Loading... |
|
804 | 805 | label_relation_new: New relation |
|
805 | 806 | label_relation_delete: Delete relation |
|
806 | 807 | label_relates_to: Related to |
|
807 | 808 | label_duplicates: Duplicates |
|
808 | 809 | label_duplicated_by: Duplicated by |
|
809 | 810 | label_blocks: Blocks |
|
810 | 811 | label_blocked_by: Blocked by |
|
811 | 812 | label_precedes: Precedes |
|
812 | 813 | label_follows: Follows |
|
813 | 814 | label_copied_to: Copied to |
|
814 | 815 | label_copied_from: Copied from |
|
815 | 816 | label_stay_logged_in: Stay logged in |
|
816 | 817 | label_disabled: disabled |
|
817 | 818 | label_show_completed_versions: Show completed versions |
|
818 | 819 | label_me: me |
|
819 | 820 | label_board: Forum |
|
820 | 821 | label_board_new: New forum |
|
821 | 822 | label_board_plural: Forums |
|
822 | 823 | label_board_locked: Locked |
|
823 | 824 | label_board_sticky: Sticky |
|
824 | 825 | label_topic_plural: Topics |
|
825 | 826 | label_message_plural: Messages |
|
826 | 827 | label_message_last: Last message |
|
827 | 828 | label_message_new: New message |
|
828 | 829 | label_message_posted: Message added |
|
829 | 830 | label_reply_plural: Replies |
|
830 | 831 | label_send_information: Send account information to the user |
|
831 | 832 | label_year: Year |
|
832 | 833 | label_month: Month |
|
833 | 834 | label_week: Week |
|
834 | 835 | label_date_from: From |
|
835 | 836 | label_date_to: To |
|
836 | 837 | label_language_based: Based on user's language |
|
837 | 838 | label_sort_by: "Sort by %{value}" |
|
838 | 839 | label_send_test_email: Send a test email |
|
839 | 840 | label_feeds_access_key: Atom access key |
|
840 | 841 | label_missing_feeds_access_key: Missing a Atom access key |
|
841 | 842 | label_feeds_access_key_created_on: "Atom access key created %{value} ago" |
|
842 | 843 | label_module_plural: Modules |
|
843 | 844 | label_added_time_by: "Added by %{author} %{age} ago" |
|
844 | 845 | label_updated_time_by: "Updated by %{author} %{age} ago" |
|
845 | 846 | label_updated_time: "Updated %{value} ago" |
|
846 | 847 | label_jump_to_a_project: Jump to a project... |
|
847 | 848 | label_file_plural: Files |
|
848 | 849 | label_changeset_plural: Changesets |
|
849 | 850 | label_default_columns: Default columns |
|
850 | 851 | label_no_change_option: (No change) |
|
851 | 852 | label_bulk_edit_selected_issues: Bulk edit selected issues |
|
852 | 853 | label_bulk_edit_selected_time_entries: Bulk edit selected time entries |
|
853 | 854 | label_theme: Theme |
|
854 | 855 | label_default: Default |
|
855 | 856 | label_search_titles_only: Search titles only |
|
856 | 857 | label_user_mail_option_all: "For any event on all my projects" |
|
857 | 858 | label_user_mail_option_selected: "For any event on the selected projects only..." |
|
858 | 859 | label_user_mail_option_none: "No events" |
|
859 | 860 | label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" |
|
860 | 861 | label_user_mail_option_only_assigned: "Only for things I am assigned to" |
|
861 | 862 | label_user_mail_option_only_owner: "Only for things I am the owner of" |
|
862 | 863 | label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself" |
|
863 | 864 | label_registration_activation_by_email: account activation by email |
|
864 | 865 | label_registration_manual_activation: manual account activation |
|
865 | 866 | label_registration_automatic_activation: automatic account activation |
|
866 | 867 | label_display_per_page: "Per page: %{value}" |
|
867 | 868 | label_age: Age |
|
868 | 869 | label_change_properties: Change properties |
|
869 | 870 | label_general: General |
|
870 | 871 | label_more: More |
|
871 | 872 | label_scm: SCM |
|
872 | 873 | label_plugins: Plugins |
|
873 | 874 | label_ldap_authentication: LDAP authentication |
|
874 | 875 | label_downloads_abbr: D/L |
|
875 | 876 | label_optional_description: Optional description |
|
876 | 877 | label_add_another_file: Add another file |
|
877 | 878 | label_preferences: Preferences |
|
878 | 879 | label_chronological_order: In chronological order |
|
879 | 880 | label_reverse_chronological_order: In reverse chronological order |
|
880 | 881 | label_planning: Planning |
|
881 | 882 | label_incoming_emails: Incoming emails |
|
882 | 883 | label_generate_key: Generate a key |
|
883 | 884 | label_issue_watchers: Watchers |
|
884 | 885 | label_example: Example |
|
885 | 886 | label_display: Display |
|
886 | 887 | label_sort: Sort |
|
887 | 888 | label_ascending: Ascending |
|
888 | 889 | label_descending: Descending |
|
889 | 890 | label_date_from_to: From %{start} to %{end} |
|
890 | 891 | label_wiki_content_added: Wiki page added |
|
891 | 892 | label_wiki_content_updated: Wiki page updated |
|
892 | 893 | label_group: Group |
|
893 | 894 | label_group_plural: Groups |
|
894 | 895 | label_group_new: New group |
|
895 | 896 | label_group_anonymous: Anonymous users |
|
896 | 897 | label_group_non_member: Non member users |
|
897 | 898 | label_time_entry_plural: Spent time |
|
898 | 899 | label_version_sharing_none: Not shared |
|
899 | 900 | label_version_sharing_descendants: With subprojects |
|
900 | 901 | label_version_sharing_hierarchy: With project hierarchy |
|
901 | 902 | label_version_sharing_tree: With project tree |
|
902 | 903 | label_version_sharing_system: With all projects |
|
903 | 904 | label_update_issue_done_ratios: Update issue done ratios |
|
904 | 905 | label_copy_source: Source |
|
905 | 906 | label_copy_target: Target |
|
906 | 907 | label_copy_same_as_target: Same as target |
|
907 | 908 | label_display_used_statuses_only: Only display statuses that are used by this tracker |
|
908 | 909 | label_api_access_key: API access key |
|
909 | 910 | label_missing_api_access_key: Missing an API access key |
|
910 | 911 | label_api_access_key_created_on: "API access key created %{value} ago" |
|
911 | 912 | label_profile: Profile |
|
912 | 913 | label_subtask_plural: Subtasks |
|
913 | 914 | label_project_copy_notifications: Send email notifications during the project copy |
|
914 | 915 | label_principal_search: "Search for user or group:" |
|
915 | 916 | label_user_search: "Search for user:" |
|
916 | 917 | label_additional_workflow_transitions_for_author: Additional transitions allowed when the user is the author |
|
917 | 918 | label_additional_workflow_transitions_for_assignee: Additional transitions allowed when the user is the assignee |
|
918 | 919 | label_issues_visibility_all: All issues |
|
919 | 920 | label_issues_visibility_public: All non private issues |
|
920 | 921 | label_issues_visibility_own: Issues created by or assigned to the user |
|
921 | 922 | label_git_report_last_commit: Report last commit for files and directories |
|
922 | 923 | label_parent_revision: Parent |
|
923 | 924 | label_child_revision: Child |
|
924 | 925 | label_export_options: "%{export_format} export options" |
|
925 | 926 | label_copy_attachments: Copy attachments |
|
926 | 927 | label_copy_subtasks: Copy subtasks |
|
927 | 928 | label_item_position: "%{position} of %{count}" |
|
928 | 929 | label_completed_versions: Completed versions |
|
929 | 930 | label_search_for_watchers: Search for watchers to add |
|
930 | 931 | label_session_expiration: Session expiration |
|
931 | 932 | label_show_closed_projects: View closed projects |
|
932 | 933 | label_status_transitions: Status transitions |
|
933 | 934 | label_fields_permissions: Fields permissions |
|
934 | 935 | label_readonly: Read-only |
|
935 | 936 | label_required: Required |
|
936 | 937 | label_hidden: Hidden |
|
937 | 938 | label_attribute_of_project: "Project's %{name}" |
|
938 | 939 | label_attribute_of_issue: "Issue's %{name}" |
|
939 | 940 | label_attribute_of_author: "Author's %{name}" |
|
940 | 941 | label_attribute_of_assigned_to: "Assignee's %{name}" |
|
941 | 942 | label_attribute_of_user: "User's %{name}" |
|
942 | 943 | label_attribute_of_fixed_version: "Target version's %{name}" |
|
943 | 944 | label_cross_project_descendants: With subprojects |
|
944 | 945 | label_cross_project_tree: With project tree |
|
945 | 946 | label_cross_project_hierarchy: With project hierarchy |
|
946 | 947 | label_cross_project_system: With all projects |
|
947 | 948 | label_gantt_progress_line: Progress line |
|
948 | 949 | label_visibility_private: to me only |
|
949 | 950 | label_visibility_roles: to these roles only |
|
950 | 951 | label_visibility_public: to any users |
|
951 | 952 | label_link: Link |
|
952 | 953 | label_only: only |
|
953 | 954 | label_drop_down_list: drop-down list |
|
954 | 955 | label_checkboxes: checkboxes |
|
955 | 956 | label_radio_buttons: radio buttons |
|
956 | 957 | label_link_values_to: Link values to URL |
|
957 | 958 | label_custom_field_select_type: Select the type of object to which the custom field is to be attached |
|
958 | 959 | label_check_for_updates: Check for updates |
|
959 | 960 | label_latest_compatible_version: Latest compatible version |
|
960 | 961 | label_unknown_plugin: Unknown plugin |
|
961 | 962 | label_add_projects: Add projects |
|
962 | 963 | label_users_visibility_all: All active users |
|
963 | 964 | label_users_visibility_members_of_visible_projects: Members of visible projects |
|
964 | 965 | label_edit_attachments: Edit attached files |
|
965 | 966 | label_link_copied_issue: Link copied issue |
|
966 | 967 | label_ask: Ask |
|
967 | 968 | label_search_attachments_yes: Search attachment filenames and descriptions |
|
968 | 969 | label_search_attachments_no: Do not search attachments |
|
969 | 970 | label_search_attachments_only: Search attachments only |
|
970 | 971 | label_search_open_issues_only: Open issues only |
|
971 | 972 | label_email_address_plural: Emails |
|
972 | 973 | label_email_address_add: Add email address |
|
973 | 974 | label_enable_notifications: Enable notifications |
|
974 | 975 | label_disable_notifications: Disable notifications |
|
975 | 976 | label_blank_value: blank |
|
976 | 977 | label_parent_task_attributes: Parent tasks attributes |
|
977 | 978 | label_parent_task_attributes_derived: Calculated from subtasks |
|
978 | 979 | label_parent_task_attributes_independent: Independent of subtasks |
|
979 | 980 | label_time_entries_visibility_all: All time entries |
|
980 | 981 | label_time_entries_visibility_own: Time entries created by the user |
|
981 | 982 | label_member_management: Member management |
|
982 | 983 | label_member_management_all_roles: All roles |
|
983 | 984 | label_member_management_selected_roles_only: Only these roles |
|
984 | 985 | label_import_issues: Import issues |
|
985 | 986 | label_select_file_to_import: Select the file to import |
|
986 | 987 | label_fields_separator: Field separator |
|
987 | 988 | label_fields_wrapper: Field wrapper |
|
988 | 989 | label_encoding: Encoding |
|
989 | 990 | label_comma_char: Comma |
|
990 | 991 | label_semi_colon_char: Semicolon |
|
991 | 992 | label_quote_char: Quote |
|
992 | 993 | label_double_quote_char: Double quote |
|
993 | 994 | label_fields_mapping: Fields mapping |
|
994 | 995 | label_file_content_preview: File content preview |
|
995 | 996 | label_create_missing_values: Create missing values |
|
996 | 997 | label_api: API |
|
997 | 998 | label_field_format_enumeration: Key/value list |
|
998 | 999 | label_default_values_for_new_users: Default values for new users |
|
999 | 1000 | label_relations: Relations |
|
1000 | 1001 | label_new_project_issue_tab_enabled: Display the "New issue" tab |
|
1001 | 1002 | label_new_object_tab_enabled: Display the "+" drop-down |
|
1002 | 1003 | label_font_default: Default font |
|
1003 | 1004 | label_font_monospace: Monospaced font |
|
1004 | 1005 | label_font_proportional: Proportional font |
|
1005 | 1006 | |
|
1006 | 1007 | button_login: Login |
|
1007 | 1008 | button_submit: Submit |
|
1008 | 1009 | button_save: Save |
|
1009 | 1010 | button_check_all: Check all |
|
1010 | 1011 | button_uncheck_all: Uncheck all |
|
1011 | 1012 | button_collapse_all: Collapse all |
|
1012 | 1013 | button_expand_all: Expand all |
|
1013 | 1014 | button_delete: Delete |
|
1014 | 1015 | button_create: Create |
|
1015 | 1016 | button_create_and_continue: Create and continue |
|
1016 | 1017 | button_test: Test |
|
1017 | 1018 | button_edit: Edit |
|
1018 | 1019 | button_edit_associated_wikipage: "Edit associated Wiki page: %{page_title}" |
|
1019 | 1020 | button_add: Add |
|
1020 | 1021 | button_change: Change |
|
1021 | 1022 | button_apply: Apply |
|
1022 | 1023 | button_clear: Clear |
|
1023 | 1024 | button_lock: Lock |
|
1024 | 1025 | button_unlock: Unlock |
|
1025 | 1026 | button_download: Download |
|
1026 | 1027 | button_list: List |
|
1027 | 1028 | button_view: View |
|
1028 | 1029 | button_move: Move |
|
1029 | 1030 | button_move_and_follow: Move and follow |
|
1030 | 1031 | button_back: Back |
|
1031 | 1032 | button_cancel: Cancel |
|
1032 | 1033 | button_activate: Activate |
|
1033 | 1034 | button_sort: Sort |
|
1034 | 1035 | button_log_time: Log time |
|
1035 | 1036 | button_rollback: Rollback to this version |
|
1036 | 1037 | button_watch: Watch |
|
1037 | 1038 | button_unwatch: Unwatch |
|
1038 | 1039 | button_reply: Reply |
|
1039 | 1040 | button_archive: Archive |
|
1040 | 1041 | button_unarchive: Unarchive |
|
1041 | 1042 | button_reset: Reset |
|
1042 | 1043 | button_rename: Rename |
|
1043 | 1044 | button_change_password: Change password |
|
1044 | 1045 | button_copy: Copy |
|
1045 | 1046 | button_copy_and_follow: Copy and follow |
|
1046 | 1047 | button_annotate: Annotate |
|
1047 | 1048 | button_update: Update |
|
1048 | 1049 | button_configure: Configure |
|
1049 | 1050 | button_quote: Quote |
|
1050 | 1051 | button_duplicate: Duplicate |
|
1051 | 1052 | button_show: Show |
|
1052 | 1053 | button_hide: Hide |
|
1053 | 1054 | button_edit_section: Edit this section |
|
1054 | 1055 | button_export: Export |
|
1055 | 1056 | button_delete_my_account: Delete my account |
|
1056 | 1057 | button_close: Close |
|
1057 | 1058 | button_reopen: Reopen |
|
1058 | 1059 | button_import: Import |
|
1059 | 1060 | button_filter: Filter |
|
1060 | 1061 | |
|
1061 | 1062 | status_active: active |
|
1062 | 1063 | status_registered: registered |
|
1063 | 1064 | status_locked: locked |
|
1064 | 1065 | |
|
1065 | 1066 | project_status_active: active |
|
1066 | 1067 | project_status_closed: closed |
|
1067 | 1068 | project_status_archived: archived |
|
1068 | 1069 | |
|
1069 | 1070 | version_status_open: open |
|
1070 | 1071 | version_status_locked: locked |
|
1071 | 1072 | version_status_closed: closed |
|
1072 | 1073 | |
|
1073 | 1074 | field_active: Active |
|
1074 | 1075 | |
|
1075 | 1076 | text_select_mail_notifications: Select actions for which email notifications should be sent. |
|
1076 | 1077 | text_regexp_info: eg. ^[A-Z0-9]+$ |
|
1077 | 1078 | text_min_max_length_info: 0 means no restriction |
|
1078 | 1079 | text_project_destroy_confirmation: Are you sure you want to delete this project and related data? |
|
1079 | 1080 | text_subprojects_destroy_warning: "Its subproject(s): %{value} will be also deleted." |
|
1080 | 1081 | text_workflow_edit: Select a role and a tracker to edit the workflow |
|
1081 | 1082 | text_are_you_sure: Are you sure? |
|
1082 | 1083 | text_journal_changed: "%{label} changed from %{old} to %{new}" |
|
1083 | 1084 | text_journal_changed_no_detail: "%{label} updated" |
|
1084 | 1085 | text_journal_set_to: "%{label} set to %{value}" |
|
1085 | 1086 | text_journal_deleted: "%{label} deleted (%{old})" |
|
1086 | 1087 | text_journal_added: "%{label} %{value} added" |
|
1087 | 1088 | text_tip_issue_begin_day: issue beginning this day |
|
1088 | 1089 | text_tip_issue_end_day: issue ending this day |
|
1089 | 1090 | text_tip_issue_begin_end_day: issue beginning and ending this day |
|
1090 | 1091 | 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.' |
|
1091 | 1092 | text_caracters_maximum: "%{count} characters maximum." |
|
1092 | 1093 | text_caracters_minimum: "Must be at least %{count} characters long." |
|
1093 | 1094 | text_length_between: "Length between %{min} and %{max} characters." |
|
1094 | 1095 | text_tracker_no_workflow: No workflow defined for this tracker |
|
1095 | 1096 | text_unallowed_characters: Unallowed characters |
|
1096 | 1097 | text_comma_separated: Multiple values allowed (comma separated). |
|
1097 | 1098 | text_line_separated: Multiple values allowed (one line for each value). |
|
1098 | 1099 | text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages |
|
1099 | 1100 | text_issue_added: "Issue %{id} has been reported by %{author}." |
|
1100 | 1101 | text_issue_updated: "Issue %{id} has been updated by %{author}." |
|
1101 | 1102 | text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content? |
|
1102 | 1103 | text_issue_category_destroy_question: "Some issues (%{count}) are assigned to this category. What do you want to do?" |
|
1103 | 1104 | text_issue_category_destroy_assignments: Remove category assignments |
|
1104 | 1105 | text_issue_category_reassign_to: Reassign issues to this category |
|
1105 | 1106 | 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)." |
|
1106 | 1107 | 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." |
|
1107 | 1108 | text_load_default_configuration: Load the default configuration |
|
1108 | 1109 | text_status_changed_by_changeset: "Applied in changeset %{value}." |
|
1109 | 1110 | text_time_logged_by_changeset: "Applied in changeset %{value}." |
|
1110 | 1111 | text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s)?' |
|
1111 | 1112 | text_issues_destroy_descendants_confirmation: "This will also delete %{count} subtask(s)." |
|
1112 | 1113 | text_time_entries_destroy_confirmation: 'Are you sure you want to delete the selected time entr(y/ies)?' |
|
1113 | 1114 | text_select_project_modules: 'Select modules to enable for this project:' |
|
1114 | 1115 | text_default_administrator_account_changed: Default administrator account changed |
|
1115 | 1116 | text_file_repository_writable: Attachments directory writable |
|
1116 | 1117 | text_plugin_assets_writable: Plugin assets directory writable |
|
1117 | 1118 | text_rmagick_available: RMagick available (optional) |
|
1118 | 1119 | text_convert_available: ImageMagick convert available (optional) |
|
1119 | 1120 | text_destroy_time_entries_question: "%{hours} hours were reported on the issues you are about to delete. What do you want to do?" |
|
1120 | 1121 | text_destroy_time_entries: Delete reported hours |
|
1121 | 1122 | text_assign_time_entries_to_project: Assign reported hours to the project |
|
1122 | 1123 | text_reassign_time_entries: 'Reassign reported hours to this issue:' |
|
1123 | 1124 | text_user_wrote: "%{value} wrote:" |
|
1124 | 1125 | text_enumeration_destroy_question: "%{count} objects are assigned to the value β%{name}β." |
|
1125 | 1126 | text_enumeration_category_reassign_to: 'Reassign them to this value:' |
|
1126 | 1127 | 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." |
|
1127 | 1128 | 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." |
|
1128 | 1129 | text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.' |
|
1129 | 1130 | text_custom_field_possible_values_info: 'One line for each value' |
|
1130 | 1131 | text_wiki_page_destroy_question: "This page has %{descendants} child page(s) and descendant(s). What do you want to do?" |
|
1131 | 1132 | text_wiki_page_nullify_children: "Keep child pages as root pages" |
|
1132 | 1133 | text_wiki_page_destroy_children: "Delete child pages and all their descendants" |
|
1133 | 1134 | text_wiki_page_reassign_children: "Reassign child pages to this parent page" |
|
1134 | 1135 | 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?" |
|
1135 | 1136 | text_zoom_in: Zoom in |
|
1136 | 1137 | text_zoom_out: Zoom out |
|
1137 | 1138 | text_warn_on_leaving_unsaved: "The current page contains unsaved text that will be lost if you leave this page." |
|
1138 | 1139 | text_scm_path_encoding_note: "Default: UTF-8" |
|
1139 | 1140 | text_subversion_repository_note: "Examples: file:///, http://, https://, svn://, svn+[tunnelscheme]://" |
|
1140 | 1141 | text_git_repository_note: Repository is bare and local (e.g. /gitrepo, c:\gitrepo) |
|
1141 | 1142 | text_mercurial_repository_note: Local repository (e.g. /hgrepo, c:\hgrepo) |
|
1142 | 1143 | text_scm_command: Command |
|
1143 | 1144 | text_scm_command_version: Version |
|
1144 | 1145 | text_scm_config: You can configure your SCM commands in config/configuration.yml. Please restart the application after editing it. |
|
1145 | 1146 | text_scm_command_not_available: SCM command is not available. Please check settings on the administration panel. |
|
1146 | 1147 | text_issue_conflict_resolution_overwrite: "Apply my changes anyway (previous notes will be kept but some changes may be overwritten)" |
|
1147 | 1148 | text_issue_conflict_resolution_add_notes: "Add my notes and discard my other changes" |
|
1148 | 1149 | text_issue_conflict_resolution_cancel: "Discard all my changes and redisplay %{link}" |
|
1149 | 1150 | text_account_destroy_confirmation: "Are you sure you want to proceed?\nYour account will be permanently deleted, with no way to reactivate it." |
|
1150 | 1151 | text_session_expiration_settings: "Warning: changing these settings may expire the current sessions including yours." |
|
1151 | 1152 | text_project_closed: This project is closed and read-only. |
|
1152 | 1153 | text_turning_multiple_off: "If you disable multiple values, multiple values will be removed in order to preserve only one value per item." |
|
1153 | 1154 | |
|
1154 | 1155 | default_role_manager: Manager |
|
1155 | 1156 | default_role_developer: Developer |
|
1156 | 1157 | default_role_reporter: Reporter |
|
1157 | 1158 | default_tracker_bug: Bug |
|
1158 | 1159 | default_tracker_feature: Feature |
|
1159 | 1160 | default_tracker_support: Support |
|
1160 | 1161 | default_issue_status_new: New |
|
1161 | 1162 | default_issue_status_in_progress: In Progress |
|
1162 | 1163 | default_issue_status_resolved: Resolved |
|
1163 | 1164 | default_issue_status_feedback: Feedback |
|
1164 | 1165 | default_issue_status_closed: Closed |
|
1165 | 1166 | default_issue_status_rejected: Rejected |
|
1166 | 1167 | default_doc_category_user: User documentation |
|
1167 | 1168 | default_doc_category_tech: Technical documentation |
|
1168 | 1169 | default_priority_low: Low |
|
1169 | 1170 | default_priority_normal: Normal |
|
1170 | 1171 | default_priority_high: High |
|
1171 | 1172 | default_priority_urgent: Urgent |
|
1172 | 1173 | default_priority_immediate: Immediate |
|
1173 | 1174 | default_activity_design: Design |
|
1174 | 1175 | default_activity_development: Development |
|
1175 | 1176 | |
|
1176 | 1177 | enumeration_issue_priorities: Issue priorities |
|
1177 | 1178 | enumeration_doc_categories: Document categories |
|
1178 | 1179 | enumeration_activities: Activities (time tracking) |
|
1179 | 1180 | enumeration_system_activity: System Activity |
|
1180 | 1181 | description_filter: Filter |
|
1181 | 1182 | description_search: Searchfield |
|
1182 | 1183 | description_choose_project: Projects |
|
1183 | 1184 | description_project_scope: Search scope |
|
1184 | 1185 | description_notes: Notes |
|
1185 | 1186 | description_message_content: Message content |
|
1186 | 1187 | description_query_sort_criteria_attribute: Sort attribute |
|
1187 | 1188 | description_query_sort_criteria_direction: Sort direction |
|
1188 | 1189 | description_user_mail_notification: Mail notification settings |
|
1189 | 1190 | description_available_columns: Available Columns |
|
1190 | 1191 | description_selected_columns: Selected Columns |
|
1191 | 1192 | description_all_columns: All Columns |
|
1192 | 1193 | description_issue_category_reassign: Choose issue category |
|
1193 | 1194 | description_wiki_subpages_reassign: Choose new parent page |
|
1194 | 1195 | description_date_range_list: Choose range from list |
|
1195 | 1196 | description_date_range_interval: Choose range by selecting start and end date |
|
1196 | 1197 | description_date_from: Enter start date |
|
1197 | 1198 | description_date_to: Enter end date |
|
1198 | 1199 | 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,1218 +1,1219 | |||
|
1 | 1 | # French translations for Ruby on Rails |
|
2 | 2 | # by Christian Lescuyer (christian@flyingcoders.com) |
|
3 | 3 | # contributor: Sebastien Grosjean - ZenCocoon.com |
|
4 | 4 | # contributor: Thibaut Cuvelier - Developpez.com |
|
5 | 5 | |
|
6 | 6 | fr: |
|
7 | 7 | direction: ltr |
|
8 | 8 | date: |
|
9 | 9 | formats: |
|
10 | 10 | default: "%d/%m/%Y" |
|
11 | 11 | short: "%e %b" |
|
12 | 12 | long: "%e %B %Y" |
|
13 | 13 | long_ordinal: "%e %B %Y" |
|
14 | 14 | only_day: "%e" |
|
15 | 15 | |
|
16 | 16 | day_names: [dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi] |
|
17 | 17 | abbr_day_names: [dim, lun, mar, mer, jeu, ven, sam] |
|
18 | 18 | |
|
19 | 19 | # Don't forget the nil at the beginning; there's no such thing as a 0th month |
|
20 | 20 | month_names: [~, janvier, fΓ©vrier, mars, avril, mai, juin, juillet, aoΓ»t, septembre, octobre, novembre, dΓ©cembre] |
|
21 | 21 | abbr_month_names: [~, jan., fΓ©v., mar., avr., mai, juin, juil., aoΓ»t, sept., oct., nov., dΓ©c.] |
|
22 | 22 | # Used in date_select and datime_select. |
|
23 | 23 | order: |
|
24 | 24 | - :day |
|
25 | 25 | - :month |
|
26 | 26 | - :year |
|
27 | 27 | |
|
28 | 28 | time: |
|
29 | 29 | formats: |
|
30 | 30 | default: "%d/%m/%Y %H:%M" |
|
31 | 31 | time: "%H:%M" |
|
32 | 32 | short: "%d %b %H:%M" |
|
33 | 33 | long: "%A %d %B %Y %H:%M:%S %Z" |
|
34 | 34 | long_ordinal: "%A %d %B %Y %H:%M:%S %Z" |
|
35 | 35 | only_second: "%S" |
|
36 | 36 | am: 'am' |
|
37 | 37 | pm: 'pm' |
|
38 | 38 | |
|
39 | 39 | datetime: |
|
40 | 40 | distance_in_words: |
|
41 | 41 | half_a_minute: "30 secondes" |
|
42 | 42 | less_than_x_seconds: |
|
43 | 43 | zero: "moins d'une seconde" |
|
44 | 44 | one: "moins d'uneΒ seconde" |
|
45 | 45 | other: "moins de %{count}Β secondes" |
|
46 | 46 | x_seconds: |
|
47 | 47 | one: "1Β seconde" |
|
48 | 48 | other: "%{count}Β secondes" |
|
49 | 49 | less_than_x_minutes: |
|
50 | 50 | zero: "moins d'une minute" |
|
51 | 51 | one: "moins d'uneΒ minute" |
|
52 | 52 | other: "moins de %{count}Β minutes" |
|
53 | 53 | x_minutes: |
|
54 | 54 | one: "1Β minute" |
|
55 | 55 | other: "%{count}Β minutes" |
|
56 | 56 | about_x_hours: |
|
57 | 57 | one: "environ une heure" |
|
58 | 58 | other: "environ %{count}Β heures" |
|
59 | 59 | x_hours: |
|
60 | 60 | one: "une heure" |
|
61 | 61 | other: "%{count}Β heures" |
|
62 | 62 | x_days: |
|
63 | 63 | one: "unΒ jour" |
|
64 | 64 | other: "%{count}Β jours" |
|
65 | 65 | about_x_months: |
|
66 | 66 | one: "environ un mois" |
|
67 | 67 | other: "environ %{count}Β mois" |
|
68 | 68 | x_months: |
|
69 | 69 | one: "unΒ mois" |
|
70 | 70 | other: "%{count}Β mois" |
|
71 | 71 | about_x_years: |
|
72 | 72 | one: "environ un an" |
|
73 | 73 | other: "environ %{count}Β ans" |
|
74 | 74 | over_x_years: |
|
75 | 75 | one: "plus d'un an" |
|
76 | 76 | other: "plus de %{count}Β ans" |
|
77 | 77 | almost_x_years: |
|
78 | 78 | one: "presqu'un an" |
|
79 | 79 | other: "presque %{count} ans" |
|
80 | 80 | prompts: |
|
81 | 81 | year: "AnnΓ©e" |
|
82 | 82 | month: "Mois" |
|
83 | 83 | day: "Jour" |
|
84 | 84 | hour: "Heure" |
|
85 | 85 | minute: "Minute" |
|
86 | 86 | second: "Seconde" |
|
87 | 87 | |
|
88 | 88 | number: |
|
89 | 89 | format: |
|
90 | 90 | precision: 3 |
|
91 | 91 | separator: ',' |
|
92 | 92 | delimiter: 'Β ' |
|
93 | 93 | currency: |
|
94 | 94 | format: |
|
95 | 95 | unit: 'β¬' |
|
96 | 96 | precision: 2 |
|
97 | 97 | format: '%nΒ %u' |
|
98 | 98 | human: |
|
99 | 99 | format: |
|
100 | 100 | precision: 3 |
|
101 | 101 | storage_units: |
|
102 | 102 | format: "%n %u" |
|
103 | 103 | units: |
|
104 | 104 | byte: |
|
105 | 105 | one: "octet" |
|
106 | 106 | other: "octets" |
|
107 | 107 | kb: "ko" |
|
108 | 108 | mb: "Mo" |
|
109 | 109 | gb: "Go" |
|
110 | 110 | tb: "To" |
|
111 | 111 | |
|
112 | 112 | support: |
|
113 | 113 | array: |
|
114 | 114 | sentence_connector: 'et' |
|
115 | 115 | skip_last_comma: true |
|
116 | 116 | word_connector: ", " |
|
117 | 117 | two_words_connector: " et " |
|
118 | 118 | last_word_connector: " et " |
|
119 | 119 | |
|
120 | 120 | activerecord: |
|
121 | 121 | errors: |
|
122 | 122 | template: |
|
123 | 123 | header: |
|
124 | 124 | one: "Impossible d'enregistrer %{model} : une erreur" |
|
125 | 125 | other: "Impossible d'enregistrer %{model} : %{count} erreurs." |
|
126 | 126 | body: "Veuillez vΓ©rifier les champs suivantsΒ :" |
|
127 | 127 | messages: |
|
128 | 128 | inclusion: "n'est pas inclus(e) dans la liste" |
|
129 | 129 | exclusion: "n'est pas disponible" |
|
130 | 130 | invalid: "n'est pas valide" |
|
131 | 131 | confirmation: "ne concorde pas avec la confirmation" |
|
132 | 132 | accepted: "doit Γͺtre acceptΓ©(e)" |
|
133 | 133 | empty: "doit Γͺtre renseignΓ©(e)" |
|
134 | 134 | blank: "doit Γͺtre renseignΓ©(e)" |
|
135 | 135 | too_long: "est trop long (pas plus de %{count} caractères)" |
|
136 | 136 | too_short: "est trop court (au moins %{count} caractères)" |
|
137 | 137 | wrong_length: "ne fait pas la bonne longueur (doit comporter %{count} caractères)" |
|
138 | 138 | taken: "est dΓ©jΓ utilisΓ©" |
|
139 | 139 | not_a_number: "n'est pas un nombre" |
|
140 | 140 | not_a_date: "n'est pas une date valide" |
|
141 | 141 | greater_than: "doit Γͺtre supΓ©rieur Γ %{count}" |
|
142 | 142 | greater_than_or_equal_to: "doit Γͺtre supΓ©rieur ou Γ©gal Γ %{count}" |
|
143 | 143 | equal_to: "doit Γͺtre Γ©gal Γ %{count}" |
|
144 | 144 | less_than: "doit Γͺtre infΓ©rieur Γ %{count}" |
|
145 | 145 | less_than_or_equal_to: "doit Γͺtre infΓ©rieur ou Γ©gal Γ %{count}" |
|
146 | 146 | odd: "doit Γͺtre impair" |
|
147 | 147 | even: "doit Γͺtre pair" |
|
148 | 148 | greater_than_start_date: "doit Γͺtre postΓ©rieure Γ la date de dΓ©but" |
|
149 | 149 | not_same_project: "n'appartient pas au mΓͺme projet" |
|
150 | 150 | circular_dependency: "Cette relation crΓ©erait une dΓ©pendance circulaire" |
|
151 | 151 | cant_link_an_issue_with_a_descendant: "Une demande ne peut pas Γͺtre liΓ©e Γ l'une de ses sous-tΓ’ches" |
|
152 | 152 | earlier_than_minimum_start_date: "ne peut pas Γͺtre antΓ©rieure au %{date} Γ cause des demandes qui prΓ©cΓ¨dent" |
|
153 | 153 | |
|
154 | 154 | actionview_instancetag_blank_option: Choisir |
|
155 | 155 | |
|
156 | 156 | general_text_No: 'Non' |
|
157 | 157 | general_text_Yes: 'Oui' |
|
158 | 158 | general_text_no: 'non' |
|
159 | 159 | general_text_yes: 'oui' |
|
160 | 160 | general_lang_name: 'French (FranΓ§ais)' |
|
161 | 161 | general_csv_separator: ';' |
|
162 | 162 | general_csv_decimal_separator: ',' |
|
163 | 163 | general_csv_encoding: ISO-8859-1 |
|
164 | 164 | general_pdf_fontname: freesans |
|
165 | 165 | general_pdf_monospaced_fontname: freemono |
|
166 | 166 | general_first_day_of_week: '1' |
|
167 | 167 | |
|
168 | 168 | notice_account_updated: Le compte a été mis à jour avec succès. |
|
169 | 169 | notice_account_invalid_credentials: Identifiant ou mot de passe invalide. |
|
170 | 170 | notice_account_password_updated: Mot de passe mis à jour avec succès. |
|
171 | 171 | notice_account_wrong_password: Mot de passe incorrect |
|
172 | 172 | notice_account_register_done: Un message contenant les instructions pour activer votre compte vous a Γ©tΓ© envoyΓ© Γ l'adresse %{email}. |
|
173 | 173 | notice_account_unknown_email: Aucun compte ne correspond Γ cette adresse. |
|
174 | 174 | notice_account_not_activated_yet: Vous n'avez pas encore activΓ© votre compte. Si vous voulez recevoir un nouveau message d'activation, veuillez <a href="%{url}">cliquer sur ce lien</a>. |
|
175 | 175 | notice_account_locked: Votre compte est verrouillΓ©. |
|
176 | 176 | notice_can_t_change_password: Ce compte utilise une authentification externe. Impossible de changer le mot de passe. |
|
177 | 177 | notice_account_lost_email_sent: Un message contenant les instructions pour choisir un nouveau mot de passe vous a Γ©tΓ© envoyΓ©. |
|
178 | 178 | notice_account_activated: Votre compte a Γ©tΓ© activΓ©. Vous pouvez Γ prΓ©sent vous connecter. |
|
179 | 179 | notice_successful_create: Création effectuée avec succès. |
|
180 | 180 | notice_successful_update: Mise à jour effectuée avec succès. |
|
181 | 181 | notice_successful_delete: Suppression effectuée avec succès. |
|
182 | 182 | notice_successful_connection: Connexion rΓ©ussie. |
|
183 | 183 | notice_file_not_found: "La page Γ laquelle vous souhaitez accΓ©der n'existe pas ou a Γ©tΓ© supprimΓ©e." |
|
184 | 184 | notice_locking_conflict: Les donnΓ©es ont Γ©tΓ© mises Γ jour par un autre utilisateur. Mise Γ jour impossible. |
|
185 | 185 | notice_not_authorized: "Vous n'Γͺtes pas autorisΓ© Γ accΓ©der Γ cette page." |
|
186 | 186 | notice_not_authorized_archived_project: Le projet auquel vous tentez d'accΓ©der a Γ©tΓ© archivΓ©. |
|
187 | 187 | notice_email_sent: "Un email a Γ©tΓ© envoyΓ© Γ %{value}" |
|
188 | 188 | notice_email_error: "Erreur lors de l'envoi de l'email (%{value})" |
|
189 | 189 | notice_feeds_access_key_reseted: "Votre clé d'accès aux flux Atom a été réinitialisée." |
|
190 | 190 | notice_api_access_key_reseted: Votre clé d'accès API a été réinitialisée. |
|
191 | 191 | notice_failed_to_save_issues: "%{count} demande(s) sur les %{total} sΓ©lectionnΓ©es n'ont pas pu Γͺtre mise(s) Γ jour : %{ids}." |
|
192 | 192 | notice_failed_to_save_time_entries: "%{count} temps passΓ©(s) sur les %{total} sΓ©lectionnΓ©s n'ont pas pu Γͺtre mis Γ jour: %{ids}." |
|
193 | 193 | notice_failed_to_save_members: "Erreur lors de la sauvegarde des membres: %{errors}." |
|
194 | 194 | notice_no_issue_selected: "Aucune demande sΓ©lectionnΓ©e ! Cochez les demandes que vous voulez mettre Γ jour." |
|
195 | 195 | notice_account_pending: "Votre compte a été créé et attend l'approbation de l'administrateur." |
|
196 | 196 | notice_default_data_loaded: Paramétrage par défaut chargé avec succès. |
|
197 | 197 | notice_unable_delete_version: Impossible de supprimer cette version. |
|
198 | 198 | notice_unable_delete_time_entry: Impossible de supprimer le temps passΓ©. |
|
199 | 199 | notice_issue_done_ratios_updated: L'avancement des demandes a Γ©tΓ© mis Γ jour. |
|
200 | 200 | notice_gantt_chart_truncated: "Le diagramme a Γ©tΓ© tronquΓ© car il excΓ¨de le nombre maximal d'Γ©lΓ©ments pouvant Γͺtre affichΓ©s (%{max})" |
|
201 | 201 | notice_issue_successful_create: "Demande %{id} créée." |
|
202 | 202 | notice_issue_update_conflict: "La demande a Γ©tΓ© mise Γ jour par un autre utilisateur pendant que vous la modifiez." |
|
203 | 203 | notice_account_deleted: "Votre compte a Γ©tΓ© dΓ©finitivement supprimΓ©." |
|
204 | 204 | notice_user_successful_create: "Utilisateur %{id} créé." |
|
205 | 205 | notice_new_password_must_be_different: Votre nouveau mot de passe doit Γͺtre diffΓ©rent de votre mot de passe actuel |
|
206 | 206 | notice_import_finished: "%{count} Γ©lΓ©ments ont Γ©tΓ© importΓ©(s)" |
|
207 | 207 | notice_import_finished_with_errors: "%{count} Γ©lΓ©ment(s) sur %{total} n'ont pas pu Γͺtre importΓ©(s)" |
|
208 | 208 | |
|
209 | 209 | error_can_t_load_default_data: "Une erreur s'est produite lors du chargement du paramΓ©trage : %{value}" |
|
210 | 210 | error_scm_not_found: "L'entrΓ©e et/ou la rΓ©vision demandΓ©e n'existe pas dans le dΓ©pΓ΄t." |
|
211 | 211 | error_scm_command_failed: "Une erreur s'est produite lors de l'accès au dépôt : %{value}" |
|
212 | 212 | error_scm_annotate: "L'entrΓ©e n'existe pas ou ne peut pas Γͺtre annotΓ©e." |
|
213 | 213 | error_scm_annotate_big_text_file: Cette entrΓ©e ne peut pas Γͺtre annotΓ©e car elle excΓ¨de la taille maximale. |
|
214 | 214 | error_issue_not_found_in_project: "La demande n'existe pas ou n'appartient pas Γ ce projet" |
|
215 | 215 | error_no_tracker_in_project: "Aucun tracker n'est associΓ© Γ ce projet. VΓ©rifier la configuration du projet." |
|
216 | 216 | error_no_default_issue_status: "Aucun statut de demande n'est dΓ©fini par dΓ©faut. VΓ©rifier votre configuration (Administration -> Statuts de demandes)." |
|
217 | 217 | error_can_not_delete_custom_field: Impossible de supprimer le champ personnalisΓ© |
|
218 | 218 | error_can_not_delete_tracker: Ce tracker contient des demandes et ne peut pas Γͺtre supprimΓ©. |
|
219 | 219 | error_can_not_remove_role: Ce rΓ΄le est utilisΓ© et ne peut pas Γͺtre supprimΓ©. |
|
220 | 220 | error_can_not_reopen_issue_on_closed_version: 'Une demande assignΓ©e Γ une version fermΓ©e ne peut pas Γͺtre rΓ©ouverte' |
|
221 | 221 | error_can_not_archive_project: "Ce projet ne peut pas Γͺtre archivΓ©" |
|
222 | 222 | error_issue_done_ratios_not_updated: L'avancement des demandes n'a pas pu Γͺtre mis Γ jour. |
|
223 | 223 | error_workflow_copy_source: 'Veuillez sΓ©lectionner un tracker et/ou un rΓ΄le source' |
|
224 | 224 | error_workflow_copy_target: 'Veuillez sΓ©lectionner les trackers et rΓ΄les cibles' |
|
225 | 225 | error_unable_delete_issue_status: Impossible de supprimer le statut de demande |
|
226 | 226 | error_unable_to_connect: Connexion impossible (%{value}) |
|
227 | 227 | error_attachment_too_big: Ce fichier ne peut pas Γͺtre attachΓ© car il excΓ¨de la taille maximale autorisΓ©e (%{max_size}) |
|
228 | 228 | error_session_expired: "Votre session a expirΓ©. Veuillez vous reconnecter." |
|
229 | 229 | warning_attachments_not_saved: "%{count} fichier(s) n'ont pas pu Γͺtre sauvegardΓ©s." |
|
230 | 230 | error_password_expired: "Votre mot de passe a expirΓ© ou nΓ©cessite d'Γͺtre changΓ©." |
|
231 | 231 | error_invalid_file_encoding: "Le fichier n'est pas un fichier %{encoding} valide" |
|
232 | 232 | error_invalid_csv_file_or_settings: "Le fichier n'est pas un fichier CSV ou n'est pas conforme aux paramètres sélectionnés" |
|
233 | 233 | error_can_not_read_import_file: "Une erreur est survenue lors de la lecture du fichier Γ importer" |
|
234 | 234 | error_attachment_extension_not_allowed: "L'extension %{extension} n'est pas autorisΓ©e" |
|
235 | 235 | error_ldap_bind_credentials: "Identifiant ou mot de passe LDAP incorrect" |
|
236 | 236 | error_no_tracker_allowed_for_new_issue_in_project: "Le projet ne dispose d'aucun tracker sur lequel vous pouvez crΓ©er une demande" |
|
237 | 237 | error_no_projects_with_tracker_allowed_for_new_issue: "Aucun projet ne dispose d'un tracker sur lequel vous pouvez crΓ©er une demande" |
|
238 | 238 | |
|
239 | 239 | mail_subject_lost_password: "Votre mot de passe %{value}" |
|
240 | 240 | mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :' |
|
241 | 241 | mail_subject_register: "Activation de votre compte %{value}" |
|
242 | 242 | mail_body_register: 'Pour activer votre compte, cliquez sur le lien suivant :' |
|
243 | 243 | mail_body_account_information_external: "Vous pouvez utiliser votre compte %{value} pour vous connecter." |
|
244 | 244 | mail_body_account_information: Paramètres de connexion de votre compte |
|
245 | 245 | mail_subject_account_activation_request: "Demande d'activation d'un compte %{value}" |
|
246 | 246 | mail_body_account_activation_request: "Un nouvel utilisateur (%{value}) s'est inscrit. Son compte nΓ©cessite votre approbation :" |
|
247 | 247 | mail_subject_reminder: "%{count} demande(s) arrivent Γ Γ©chΓ©ance (%{days})" |
|
248 | 248 | mail_body_reminder: "%{count} demande(s) qui vous sont assignΓ©es arrivent Γ Γ©chΓ©ance dans les %{days} prochains jours :" |
|
249 | 249 | mail_subject_wiki_content_added: "Page wiki '%{id}' ajoutΓ©e" |
|
250 | 250 | mail_body_wiki_content_added: "La page wiki '%{id}' a Γ©tΓ© ajoutΓ©e par %{author}." |
|
251 | 251 | mail_subject_wiki_content_updated: "Page wiki '%{id}' mise Γ jour" |
|
252 | 252 | mail_body_wiki_content_updated: "La page wiki '%{id}' a Γ©tΓ© mise Γ jour par %{author}." |
|
253 | 253 | mail_body_settings_updated: "Les paramètres suivants ont été modifiés :" |
|
254 | 254 | mail_body_password_updated: "Votre mot de passe a Γ©tΓ© changΓ©." |
|
255 | 255 | |
|
256 | 256 | field_name: Nom |
|
257 | 257 | field_description: Description |
|
258 | 258 | field_summary: RΓ©sumΓ© |
|
259 | 259 | field_is_required: Obligatoire |
|
260 | 260 | field_firstname: PrΓ©nom |
|
261 | 261 | field_lastname: Nom |
|
262 | 262 | field_mail: Email |
|
263 | 263 | field_address: Email |
|
264 | 264 | field_filename: Fichier |
|
265 | 265 | field_filesize: Taille |
|
266 | 266 | field_downloads: TΓ©lΓ©chargements |
|
267 | 267 | field_author: Auteur |
|
268 | 268 | field_created_on: Créé |
|
269 | 269 | field_updated_on: Mis-Γ -jour |
|
270 | 270 | field_closed_on: FermΓ© |
|
271 | 271 | field_field_format: Format |
|
272 | 272 | field_is_for_all: Pour tous les projets |
|
273 | 273 | field_possible_values: Valeurs possibles |
|
274 | 274 | field_regexp: Expression régulière |
|
275 | 275 | field_min_length: Longueur minimum |
|
276 | 276 | field_max_length: Longueur maximum |
|
277 | 277 | field_value: Valeur |
|
278 | 278 | field_category: CatΓ©gorie |
|
279 | 279 | field_title: Titre |
|
280 | 280 | field_project: Projet |
|
281 | 281 | field_issue: Demande |
|
282 | 282 | field_status: Statut |
|
283 | 283 | field_notes: Notes |
|
284 | 284 | field_is_closed: Demande fermΓ©e |
|
285 | 285 | field_is_default: Valeur par dΓ©faut |
|
286 | 286 | field_tracker: Tracker |
|
287 | 287 | field_subject: Sujet |
|
288 | 288 | field_due_date: EchΓ©ance |
|
289 | 289 | field_assigned_to: AssignΓ© Γ |
|
290 | 290 | field_priority: PrioritΓ© |
|
291 | 291 | field_fixed_version: Version cible |
|
292 | 292 | field_user: Utilisateur |
|
293 | 293 | field_principal: Principal |
|
294 | 294 | field_role: RΓ΄le |
|
295 | 295 | field_homepage: Site web |
|
296 | 296 | field_is_public: Public |
|
297 | 297 | field_parent: Sous-projet de |
|
298 | 298 | field_is_in_roadmap: Demandes affichΓ©es dans la roadmap |
|
299 | 299 | field_login: Identifiant |
|
300 | 300 | field_mail_notification: Notifications par mail |
|
301 | 301 | field_admin: Administrateur |
|
302 | 302 | field_last_login_on: Dernière connexion |
|
303 | 303 | field_language: Langue |
|
304 | 304 | field_effective_date: Date |
|
305 | 305 | field_password: Mot de passe |
|
306 | 306 | field_new_password: Nouveau mot de passe |
|
307 | 307 | field_password_confirmation: Confirmation |
|
308 | 308 | field_version: Version |
|
309 | 309 | field_type: Type |
|
310 | 310 | field_host: HΓ΄te |
|
311 | 311 | field_port: Port |
|
312 | 312 | field_account: Compte |
|
313 | 313 | field_base_dn: Base DN |
|
314 | 314 | field_attr_login: Attribut Identifiant |
|
315 | 315 | field_attr_firstname: Attribut PrΓ©nom |
|
316 | 316 | field_attr_lastname: Attribut Nom |
|
317 | 317 | field_attr_mail: Attribut Email |
|
318 | 318 | field_onthefly: CrΓ©ation des utilisateurs Γ la volΓ©e |
|
319 | 319 | field_start_date: DΓ©but |
|
320 | 320 | field_done_ratio: "% rΓ©alisΓ©" |
|
321 | 321 | field_auth_source: Mode d'authentification |
|
322 | 322 | field_hide_mail: Cacher mon adresse mail |
|
323 | 323 | field_comments: Commentaire |
|
324 | 324 | field_url: URL |
|
325 | 325 | field_start_page: Page de dΓ©marrage |
|
326 | 326 | field_subproject: Sous-projet |
|
327 | 327 | field_hours: Heures |
|
328 | 328 | field_activity: ActivitΓ© |
|
329 | 329 | field_spent_on: Date |
|
330 | 330 | field_identifier: Identifiant |
|
331 | 331 | field_is_filter: UtilisΓ© comme filtre |
|
332 | 332 | field_issue_to: Demande liΓ©e |
|
333 | 333 | field_delay: Retard |
|
334 | 334 | field_assignable: Demandes assignables Γ ce rΓ΄le |
|
335 | 335 | field_redirect_existing_links: Rediriger les liens existants |
|
336 | 336 | field_estimated_hours: Temps estimΓ© |
|
337 | 337 | field_column_names: Colonnes |
|
338 | 338 | field_time_entries: Temps passΓ© |
|
339 | 339 | field_time_zone: Fuseau horaire |
|
340 | 340 | field_searchable: UtilisΓ© pour les recherches |
|
341 | 341 | field_default_value: Valeur par dΓ©faut |
|
342 | 342 | field_comments_sorting: Afficher les commentaires |
|
343 | 343 | field_parent_title: Page parent |
|
344 | 344 | field_editable: Modifiable |
|
345 | 345 | field_watcher: Observateur |
|
346 | 346 | field_identity_url: URL OpenID |
|
347 | 347 | field_content: Contenu |
|
348 | 348 | field_group_by: Grouper par |
|
349 | 349 | field_sharing: Partage |
|
350 | 350 | field_parent_issue: TΓ’che parente |
|
351 | 351 | field_member_of_group: Groupe de l'assignΓ© |
|
352 | 352 | field_assigned_to_role: RΓ΄le de l'assignΓ© |
|
353 | 353 | field_text: Champ texte |
|
354 | 354 | field_visible: Visible |
|
355 | 355 | field_warn_on_leaving_unsaved: "M'avertir lorsque je quitte une page contenant du texte non sauvegardΓ©" |
|
356 | 356 | field_issues_visibility: VisibilitΓ© des demandes |
|
357 | 357 | field_is_private: PrivΓ©e |
|
358 | 358 | field_commit_logs_encoding: Encodage des messages de commit |
|
359 | 359 | field_scm_path_encoding: Encodage des chemins |
|
360 | 360 | field_path_to_repository: Chemin du dΓ©pΓ΄t |
|
361 | 361 | field_root_directory: RΓ©pertoire racine |
|
362 | 362 | field_cvsroot: CVSROOT |
|
363 | 363 | field_cvs_module: Module |
|
364 | 364 | field_repository_is_default: DΓ©pΓ΄t principal |
|
365 | 365 | field_multiple: Valeurs multiples |
|
366 | 366 | field_auth_source_ldap_filter: Filtre LDAP |
|
367 | 367 | field_core_fields: Champs standards |
|
368 | 368 | field_timeout: "Timeout (en secondes)" |
|
369 | 369 | field_board_parent: Forum parent |
|
370 | 370 | field_private_notes: Notes privΓ©es |
|
371 | 371 | field_inherit_members: HΓ©riter les membres |
|
372 | 372 | field_generate_password: GΓ©nΓ©rer un mot de passe |
|
373 | 373 | field_must_change_passwd: Doit changer de mot de passe Γ la prochaine connexion |
|
374 | 374 | field_default_status: Statut par dΓ©faut |
|
375 | 375 | field_users_visibility: VisibilitΓ© des utilisateurs |
|
376 | 376 | field_time_entries_visibility: VisibilitΓ© du temps passΓ© |
|
377 | 377 | field_total_estimated_hours: Temps estimΓ© total |
|
378 | 378 | field_default_version: Version par dΓ©faut |
|
379 | 379 | field_textarea_font: Police utilisΓ©e pour les champs texte |
|
380 | 380 | |
|
381 | 381 | setting_app_title: Titre de l'application |
|
382 | 382 | setting_app_subtitle: Sous-titre de l'application |
|
383 | 383 | setting_welcome_text: Texte d'accueil |
|
384 | 384 | setting_default_language: Langue par dΓ©faut |
|
385 | 385 | setting_login_required: Authentification obligatoire |
|
386 | 386 | setting_self_registration: Inscription des nouveaux utilisateurs |
|
387 | 387 | setting_attachment_max_size: Taille maximale des fichiers |
|
388 | 388 | setting_issues_export_limit: Limite d'exportation des demandes |
|
389 | 389 | setting_mail_from: Adresse d'Γ©mission |
|
390 | 390 | setting_bcc_recipients: Destinataires en copie cachΓ©e (cci) |
|
391 | 391 | setting_plain_text_mail: Mail en texte brut (non HTML) |
|
392 | 392 | setting_host_name: Nom d'hΓ΄te et chemin |
|
393 | 393 | setting_text_formatting: Formatage du texte |
|
394 | 394 | setting_wiki_compression: Compression de l'historique des pages wiki |
|
395 | 395 | setting_feeds_limit: Nombre maximal d'Γ©lΓ©ments dans les flux Atom |
|
396 | 396 | setting_default_projects_public: DΓ©finir les nouveaux projets comme publics par dΓ©faut |
|
397 | 397 | setting_autofetch_changesets: RΓ©cupΓ©ration automatique des commits |
|
398 | 398 | setting_sys_api_enabled: Activer les WS pour la gestion des dΓ©pΓ΄ts |
|
399 | 399 | setting_commit_ref_keywords: Mots-clΓ©s de rΓ©fΓ©rencement |
|
400 | 400 | setting_commit_fix_keywords: Mots-clΓ©s de rΓ©solution |
|
401 | 401 | setting_autologin: DurΓ©e maximale de connexion automatique |
|
402 | 402 | setting_date_format: Format de date |
|
403 | 403 | setting_time_format: Format d'heure |
|
404 | setting_timespan_format: Format des temps en heures | |
|
404 | 405 | setting_cross_project_issue_relations: Autoriser les relations entre demandes de diffΓ©rents projets |
|
405 | 406 | setting_cross_project_subtasks: Autoriser les sous-tΓ’ches dans des projets diffΓ©rents |
|
406 | 407 | setting_issue_list_default_columns: Colonnes affichΓ©es par dΓ©faut sur la liste des demandes |
|
407 | 408 | setting_repositories_encodings: Encodages des fichiers et des dΓ©pΓ΄ts |
|
408 | 409 | setting_emails_header: En-tΓͺte des emails |
|
409 | 410 | setting_emails_footer: Pied-de-page des emails |
|
410 | 411 | setting_protocol: Protocole |
|
411 | 412 | setting_per_page_options: Options d'objets affichΓ©s par page |
|
412 | 413 | setting_user_format: Format d'affichage des utilisateurs |
|
413 | 414 | setting_activity_days_default: Nombre de jours affichΓ©s sur l'activitΓ© des projets |
|
414 | 415 | setting_display_subprojects_issues: Afficher par dΓ©faut les demandes des sous-projets sur les projets principaux |
|
415 | 416 | setting_enabled_scm: SCM activΓ©s |
|
416 | 417 | setting_mail_handler_body_delimiters: "Tronquer les emails après l'une de ces lignes" |
|
417 | 418 | setting_mail_handler_api_enabled: "Activer le WS pour la rΓ©ception d'emails" |
|
418 | 419 | setting_mail_handler_api_key: ClΓ© de protection de l'API |
|
419 | 420 | setting_sequential_project_identifiers: GΓ©nΓ©rer des identifiants de projet sΓ©quentiels |
|
420 | 421 | setting_gravatar_enabled: Afficher les Gravatar des utilisateurs |
|
421 | 422 | setting_gravatar_default: Image Gravatar par dΓ©faut |
|
422 | 423 | setting_diff_max_lines_displayed: Nombre maximum de lignes de diff affichΓ©es |
|
423 | 424 | setting_file_max_size_displayed: Taille maximum des fichiers texte affichΓ©s en ligne |
|
424 | 425 | setting_repository_log_display_limit: "Nombre maximum de rΓ©visions affichΓ©es sur l'historique d'un fichier" |
|
425 | 426 | setting_openid: "Autoriser l'authentification et l'enregistrement OpenID" |
|
426 | 427 | setting_password_max_age: Expiration des mots de passe après |
|
427 | 428 | setting_password_min_length: Longueur minimum des mots de passe |
|
428 | 429 | setting_new_project_user_role_id: RΓ΄le donnΓ© Γ un utilisateur non-administrateur qui crΓ©e un projet |
|
429 | 430 | setting_default_projects_modules: Modules activΓ©s par dΓ©faut pour les nouveaux projets |
|
430 | 431 | setting_issue_done_ratio: Calcul de l'avancement des demandes |
|
431 | 432 | setting_issue_done_ratio_issue_field: 'Utiliser le champ % effectuΓ©' |
|
432 | 433 | setting_issue_done_ratio_issue_status: Utiliser le statut |
|
433 | 434 | setting_start_of_week: Jour de dΓ©but des calendriers |
|
434 | 435 | setting_rest_api_enabled: Activer l'API REST |
|
435 | 436 | setting_cache_formatted_text: Mettre en cache le texte formatΓ© |
|
436 | 437 | setting_default_notification_option: Option de notification par dΓ©faut |
|
437 | 438 | setting_commit_logtime_enabled: Permettre la saisie de temps |
|
438 | 439 | setting_commit_logtime_activity_id: ActivitΓ© pour le temps saisi |
|
439 | 440 | setting_gantt_items_limit: Nombre maximum d'Γ©lΓ©ments affichΓ©s sur le gantt |
|
440 | 441 | setting_issue_group_assignment: Permettre l'assignation des demandes aux groupes |
|
441 | 442 | 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 |
|
442 | 443 | setting_commit_cross_project_ref: Permettre le rΓ©fΓ©rencement et la rΓ©solution des demandes de tous les autres projets |
|
443 | 444 | setting_unsubscribe: Permettre aux utilisateurs de supprimer leur propre compte |
|
444 | 445 | setting_session_lifetime: DurΓ©e de vie maximale des sessions |
|
445 | 446 | setting_session_timeout: DurΓ©e maximale d'inactivitΓ© |
|
446 | 447 | setting_thumbnails_enabled: Afficher les vignettes des images |
|
447 | 448 | setting_thumbnails_size: Taille des vignettes (en pixels) |
|
448 | 449 | setting_non_working_week_days: Jours non travaillΓ©s |
|
449 | 450 | setting_jsonp_enabled: Activer le support JSONP |
|
450 | 451 | setting_default_projects_tracker_ids: Trackers par dΓ©faut pour les nouveaux projets |
|
451 | 452 | setting_mail_handler_excluded_filenames: Exclure les fichiers attachΓ©s par leur nom |
|
452 | 453 | setting_force_default_language_for_anonymous: Forcer la langue par dΓ©fault pour les utilisateurs anonymes |
|
453 | 454 | setting_force_default_language_for_loggedin: Forcer la langue par dΓ©fault pour les utilisateurs identifiΓ©s |
|
454 | 455 | setting_link_copied_issue: Lier les demandes lors de la copie |
|
455 | 456 | setting_max_additional_emails: Nombre maximal d'adresses email additionnelles |
|
456 | 457 | setting_search_results_per_page: RΓ©sultats de recherche affichΓ©s par page |
|
457 | 458 | setting_attachment_extensions_allowed: Extensions autorisΓ©es |
|
458 | 459 | setting_attachment_extensions_denied: Extensions non autorisΓ©es |
|
459 | 460 | setting_sys_api_key: ClΓ© de protection de l'API |
|
460 | 461 | setting_lost_password: Autoriser la rΓ©initialisation par email de mot de passe perdu |
|
461 | 462 | setting_new_item_menu_tab: Onglet de crΓ©ation d'objets dans le menu du project |
|
462 | 463 | |
|
463 | 464 | permission_add_project: CrΓ©er un projet |
|
464 | 465 | permission_add_subprojects: CrΓ©er des sous-projets |
|
465 | 466 | permission_edit_project: Modifier le projet |
|
466 | 467 | permission_close_project: Fermer / rΓ©ouvrir le projet |
|
467 | 468 | permission_select_project_modules: Choisir les modules |
|
468 | 469 | permission_manage_members: GΓ©rer les membres |
|
469 | 470 | permission_manage_project_activities: GΓ©rer les activitΓ©s |
|
470 | 471 | permission_manage_versions: GΓ©rer les versions |
|
471 | 472 | permission_manage_categories: GΓ©rer les catΓ©gories de demandes |
|
472 | 473 | permission_view_issues: Voir les demandes |
|
473 | 474 | permission_add_issues: CrΓ©er des demandes |
|
474 | 475 | permission_edit_issues: Modifier les demandes |
|
475 | 476 | permission_copy_issues: Copier les demandes |
|
476 | 477 | permission_manage_issue_relations: GΓ©rer les relations |
|
477 | 478 | permission_set_issues_private: Rendre les demandes publiques ou privΓ©es |
|
478 | 479 | permission_set_own_issues_private: Rendre ses propres demandes publiques ou privΓ©es |
|
479 | 480 | permission_add_issue_notes: Ajouter des notes |
|
480 | 481 | permission_edit_issue_notes: Modifier les notes |
|
481 | 482 | permission_edit_own_issue_notes: Modifier ses propres notes |
|
482 | 483 | permission_view_private_notes: Voir les notes privΓ©es |
|
483 | 484 | permission_set_notes_private: Rendre les notes privΓ©es |
|
484 | 485 | permission_move_issues: DΓ©placer les demandes |
|
485 | 486 | permission_delete_issues: Supprimer les demandes |
|
486 | 487 | permission_manage_public_queries: GΓ©rer les requΓͺtes publiques |
|
487 | 488 | permission_save_queries: Sauvegarder les requΓͺtes |
|
488 | 489 | permission_view_gantt: Voir le gantt |
|
489 | 490 | permission_view_calendar: Voir le calendrier |
|
490 | 491 | permission_view_issue_watchers: Voir la liste des observateurs |
|
491 | 492 | permission_add_issue_watchers: Ajouter des observateurs |
|
492 | 493 | permission_delete_issue_watchers: Supprimer des observateurs |
|
493 | 494 | permission_log_time: Saisir le temps passΓ© |
|
494 | 495 | permission_view_time_entries: Voir le temps passΓ© |
|
495 | 496 | permission_edit_time_entries: Modifier les temps passΓ©s |
|
496 | 497 | permission_edit_own_time_entries: Modifier son propre temps passΓ© |
|
497 | 498 | permission_manage_news: GΓ©rer les annonces |
|
498 | 499 | permission_comment_news: Commenter les annonces |
|
499 | 500 | permission_view_documents: Voir les documents |
|
500 | 501 | permission_add_documents: Ajouter des documents |
|
501 | 502 | permission_edit_documents: Modifier les documents |
|
502 | 503 | permission_delete_documents: Supprimer les documents |
|
503 | 504 | permission_manage_files: GΓ©rer les fichiers |
|
504 | 505 | permission_view_files: Voir les fichiers |
|
505 | 506 | permission_manage_wiki: GΓ©rer le wiki |
|
506 | 507 | permission_rename_wiki_pages: Renommer les pages |
|
507 | 508 | permission_delete_wiki_pages: Supprimer les pages |
|
508 | 509 | permission_view_wiki_pages: Voir le wiki |
|
509 | 510 | permission_view_wiki_edits: "Voir l'historique des modifications" |
|
510 | 511 | permission_edit_wiki_pages: Modifier les pages |
|
511 | 512 | permission_delete_wiki_pages_attachments: Supprimer les fichiers joints |
|
512 | 513 | permission_protect_wiki_pages: ProtΓ©ger les pages |
|
513 | 514 | permission_manage_repository: GΓ©rer le dΓ©pΓ΄t de sources |
|
514 | 515 | permission_browse_repository: Parcourir les sources |
|
515 | 516 | permission_view_changesets: Voir les rΓ©visions |
|
516 | 517 | permission_commit_access: Droit de commit |
|
517 | 518 | permission_manage_boards: GΓ©rer les forums |
|
518 | 519 | permission_view_messages: Voir les messages |
|
519 | 520 | permission_add_messages: Poster un message |
|
520 | 521 | permission_edit_messages: Modifier les messages |
|
521 | 522 | permission_edit_own_messages: Modifier ses propres messages |
|
522 | 523 | permission_delete_messages: Supprimer les messages |
|
523 | 524 | permission_delete_own_messages: Supprimer ses propres messages |
|
524 | 525 | permission_export_wiki_pages: Exporter les pages |
|
525 | 526 | permission_manage_subtasks: GΓ©rer les sous-tΓ’ches |
|
526 | 527 | permission_manage_related_issues: GΓ©rer les demandes associΓ©es |
|
527 | 528 | permission_import_issues: Importer des demandes |
|
528 | 529 | |
|
529 | 530 | project_module_issue_tracking: Suivi des demandes |
|
530 | 531 | project_module_time_tracking: Suivi du temps passΓ© |
|
531 | 532 | project_module_news: Publication d'annonces |
|
532 | 533 | project_module_documents: Publication de documents |
|
533 | 534 | project_module_files: Publication de fichiers |
|
534 | 535 | project_module_wiki: Wiki |
|
535 | 536 | project_module_repository: DΓ©pΓ΄t de sources |
|
536 | 537 | project_module_boards: Forums de discussion |
|
537 | 538 | project_module_calendar: Calendrier |
|
538 | 539 | project_module_gantt: Gantt |
|
539 | 540 | |
|
540 | 541 | label_user: Utilisateur |
|
541 | 542 | label_user_plural: Utilisateurs |
|
542 | 543 | label_user_new: Nouvel utilisateur |
|
543 | 544 | label_user_anonymous: Anonyme |
|
544 | 545 | label_project: Projet |
|
545 | 546 | label_project_new: Nouveau projet |
|
546 | 547 | label_project_plural: Projets |
|
547 | 548 | label_x_projects: |
|
548 | 549 | zero: aucun projet |
|
549 | 550 | one: un projet |
|
550 | 551 | other: "%{count} projets" |
|
551 | 552 | label_project_all: Tous les projets |
|
552 | 553 | label_project_latest: Derniers projets |
|
553 | 554 | label_issue: Demande |
|
554 | 555 | label_issue_new: Nouvelle demande |
|
555 | 556 | label_issue_plural: Demandes |
|
556 | 557 | label_issue_view_all: Voir toutes les demandes |
|
557 | 558 | label_issues_by: "Demandes par %{value}" |
|
558 | 559 | label_issue_added: Demande ajoutΓ©e |
|
559 | 560 | label_issue_updated: Demande mise Γ jour |
|
560 | 561 | label_issue_note_added: Note ajoutΓ©e |
|
561 | 562 | label_issue_status_updated: Statut changΓ© |
|
562 | 563 | label_issue_assigned_to_updated: AssignΓ© changΓ© |
|
563 | 564 | label_issue_priority_updated: PrioritΓ© changΓ©e |
|
564 | 565 | label_document: Document |
|
565 | 566 | label_document_new: Nouveau document |
|
566 | 567 | label_document_plural: Documents |
|
567 | 568 | label_document_added: Document ajoutΓ© |
|
568 | 569 | label_role: RΓ΄le |
|
569 | 570 | label_role_plural: RΓ΄les |
|
570 | 571 | label_role_new: Nouveau rΓ΄le |
|
571 | 572 | label_role_and_permissions: RΓ΄les et permissions |
|
572 | 573 | label_role_anonymous: Anonyme |
|
573 | 574 | label_role_non_member: Non membre |
|
574 | 575 | label_member: Membre |
|
575 | 576 | label_member_new: Nouveau membre |
|
576 | 577 | label_member_plural: Membres |
|
577 | 578 | label_tracker: Tracker |
|
578 | 579 | label_tracker_plural: Trackers |
|
579 | 580 | label_tracker_all: Tous les trackers |
|
580 | 581 | label_tracker_new: Nouveau tracker |
|
581 | 582 | label_workflow: Workflow |
|
582 | 583 | label_issue_status: Statut de demandes |
|
583 | 584 | label_issue_status_plural: Statuts de demandes |
|
584 | 585 | label_issue_status_new: Nouveau statut |
|
585 | 586 | label_issue_category: CatΓ©gorie de demandes |
|
586 | 587 | label_issue_category_plural: CatΓ©gories de demandes |
|
587 | 588 | label_issue_category_new: Nouvelle catΓ©gorie |
|
588 | 589 | label_custom_field: Champ personnalisΓ© |
|
589 | 590 | label_custom_field_plural: Champs personnalisΓ©s |
|
590 | 591 | label_custom_field_new: Nouveau champ personnalisΓ© |
|
591 | 592 | label_enumerations: Listes de valeurs |
|
592 | 593 | label_enumeration_new: Nouvelle valeur |
|
593 | 594 | label_information: Information |
|
594 | 595 | label_information_plural: Informations |
|
595 | 596 | label_please_login: Identification |
|
596 | 597 | label_register: S'enregistrer |
|
597 | 598 | label_login_with_open_id_option: S'authentifier avec OpenID |
|
598 | 599 | label_password_lost: Mot de passe perdu |
|
599 | 600 | label_password_required: Confirmez votre mot de passe pour continuer |
|
600 | 601 | label_home: Accueil |
|
601 | 602 | label_my_page: Ma page |
|
602 | 603 | label_my_account: Mon compte |
|
603 | 604 | label_my_projects: Mes projets |
|
604 | 605 | label_my_page_block: Blocs disponibles |
|
605 | 606 | label_administration: Administration |
|
606 | 607 | label_login: Connexion |
|
607 | 608 | label_logout: DΓ©connexion |
|
608 | 609 | label_help: Aide |
|
609 | 610 | label_reported_issues: Demandes soumises |
|
610 | 611 | label_assigned_issues: Demandes assignΓ©es |
|
611 | 612 | label_assigned_to_me_issues: Demandes qui me sont assignΓ©es |
|
612 | 613 | label_last_login: Dernière connexion |
|
613 | 614 | label_registered_on: Inscrit le |
|
614 | 615 | label_activity: ActivitΓ© |
|
615 | 616 | label_overall_activity: ActivitΓ© globale |
|
616 | 617 | label_user_activity: "ActivitΓ© de %{value}" |
|
617 | 618 | label_new: Nouveau |
|
618 | 619 | label_logged_as: ConnectΓ© en tant que |
|
619 | 620 | label_environment: Environnement |
|
620 | 621 | label_authentication: Authentification |
|
621 | 622 | label_auth_source: Mode d'authentification |
|
622 | 623 | label_auth_source_new: Nouveau mode d'authentification |
|
623 | 624 | label_auth_source_plural: Modes d'authentification |
|
624 | 625 | label_subproject_plural: Sous-projets |
|
625 | 626 | label_subproject_new: Nouveau sous-projet |
|
626 | 627 | label_and_its_subprojects: "%{value} et ses sous-projets" |
|
627 | 628 | label_min_max_length: Longueurs mini - maxi |
|
628 | 629 | label_list: Liste |
|
629 | 630 | label_date: Date |
|
630 | 631 | label_integer: Entier |
|
631 | 632 | label_float: Nombre dΓ©cimal |
|
632 | 633 | label_boolean: BoolΓ©en |
|
633 | 634 | label_string: Texte |
|
634 | 635 | label_text: Texte long |
|
635 | 636 | label_attribute: Attribut |
|
636 | 637 | label_attribute_plural: Attributs |
|
637 | 638 | label_no_data: Aucune donnΓ©e Γ afficher |
|
638 | 639 | label_change_status: Changer le statut |
|
639 | 640 | label_history: Historique |
|
640 | 641 | label_attachment: Fichier |
|
641 | 642 | label_attachment_new: Nouveau fichier |
|
642 | 643 | label_attachment_delete: Supprimer le fichier |
|
643 | 644 | label_attachment_plural: Fichiers |
|
644 | 645 | label_file_added: Fichier ajoutΓ© |
|
645 | 646 | label_report: Rapport |
|
646 | 647 | label_report_plural: Rapports |
|
647 | 648 | label_news: Annonce |
|
648 | 649 | label_news_new: Nouvelle annonce |
|
649 | 650 | label_news_plural: Annonces |
|
650 | 651 | label_news_latest: Dernières annonces |
|
651 | 652 | label_news_view_all: Voir toutes les annonces |
|
652 | 653 | label_news_added: Annonce ajoutΓ©e |
|
653 | 654 | label_news_comment_added: Commentaire ajoutΓ© Γ une annonce |
|
654 | 655 | label_settings: Configuration |
|
655 | 656 | label_overview: AperΓ§u |
|
656 | 657 | label_version: Version |
|
657 | 658 | label_version_new: Nouvelle version |
|
658 | 659 | label_version_plural: Versions |
|
659 | 660 | label_close_versions: Fermer les versions terminΓ©es |
|
660 | 661 | label_confirmation: Confirmation |
|
661 | 662 | label_export_to: 'Formats disponibles :' |
|
662 | 663 | label_read: Lire... |
|
663 | 664 | label_public_projects: Projets publics |
|
664 | 665 | label_open_issues: ouvert |
|
665 | 666 | label_open_issues_plural: ouverts |
|
666 | 667 | label_closed_issues: fermΓ© |
|
667 | 668 | label_closed_issues_plural: fermΓ©s |
|
668 | 669 | label_x_open_issues_abbr: |
|
669 | 670 | zero: 0 ouverte |
|
670 | 671 | one: 1 ouverte |
|
671 | 672 | other: "%{count} ouvertes" |
|
672 | 673 | label_x_closed_issues_abbr: |
|
673 | 674 | zero: 0 fermΓ©e |
|
674 | 675 | one: 1 fermΓ©e |
|
675 | 676 | other: "%{count} fermΓ©es" |
|
676 | 677 | label_x_issues: |
|
677 | 678 | zero: 0 demande |
|
678 | 679 | one: 1 demande |
|
679 | 680 | other: "%{count} demandes" |
|
680 | 681 | label_total: Total |
|
681 | 682 | label_total_plural: Totaux |
|
682 | 683 | label_total_time: Temps total |
|
683 | 684 | label_permissions: Permissions |
|
684 | 685 | label_current_status: Statut actuel |
|
685 | 686 | label_new_statuses_allowed: Nouveaux statuts autorisΓ©s |
|
686 | 687 | label_all: tous |
|
687 | 688 | label_any: tous |
|
688 | 689 | label_none: aucun |
|
689 | 690 | label_nobody: personne |
|
690 | 691 | label_next: Suivant |
|
691 | 692 | label_previous: PrΓ©cΓ©dent |
|
692 | 693 | label_used_by: UtilisΓ© par |
|
693 | 694 | label_details: DΓ©tails |
|
694 | 695 | label_add_note: Ajouter une note |
|
695 | 696 | label_calendar: Calendrier |
|
696 | 697 | label_months_from: mois depuis |
|
697 | 698 | label_gantt: Gantt |
|
698 | 699 | label_internal: Interne |
|
699 | 700 | label_last_changes: "%{count} derniers changements" |
|
700 | 701 | label_change_view_all: Voir tous les changements |
|
701 | 702 | label_personalize_page: Personnaliser cette page |
|
702 | 703 | label_comment: Commentaire |
|
703 | 704 | label_comment_plural: Commentaires |
|
704 | 705 | label_x_comments: |
|
705 | 706 | zero: aucun commentaire |
|
706 | 707 | one: un commentaire |
|
707 | 708 | other: "%{count} commentaires" |
|
708 | 709 | label_comment_add: Ajouter un commentaire |
|
709 | 710 | label_comment_added: Commentaire ajoutΓ© |
|
710 | 711 | label_comment_delete: Supprimer les commentaires |
|
711 | 712 | label_query: Rapport personnalisΓ© |
|
712 | 713 | label_query_plural: Rapports personnalisΓ©s |
|
713 | 714 | label_query_new: Nouveau rapport |
|
714 | 715 | label_my_queries: Mes rapports personnalisΓ©s |
|
715 | 716 | label_filter_add: Ajouter le filtre |
|
716 | 717 | label_filter_plural: Filtres |
|
717 | 718 | label_equals: Γ©gal |
|
718 | 719 | label_not_equals: diffΓ©rent |
|
719 | 720 | label_in_less_than: dans moins de |
|
720 | 721 | label_in_more_than: dans plus de |
|
721 | 722 | label_in_the_next_days: dans les prochains jours |
|
722 | 723 | label_in_the_past_days: dans les derniers jours |
|
723 | 724 | label_greater_or_equal: '>=' |
|
724 | 725 | label_less_or_equal: '<=' |
|
725 | 726 | label_between: entre |
|
726 | 727 | label_in: dans |
|
727 | 728 | label_today: aujourd'hui |
|
728 | 729 | label_all_time: toute la pΓ©riode |
|
729 | 730 | label_yesterday: hier |
|
730 | 731 | label_this_week: cette semaine |
|
731 | 732 | label_last_week: la semaine dernière |
|
732 | 733 | label_last_n_weeks: "les %{count} dernières semaines" |
|
733 | 734 | label_last_n_days: "les %{count} derniers jours" |
|
734 | 735 | label_this_month: ce mois-ci |
|
735 | 736 | label_last_month: le mois dernier |
|
736 | 737 | label_this_year: cette annΓ©e |
|
737 | 738 | label_date_range: PΓ©riode |
|
738 | 739 | label_less_than_ago: il y a moins de |
|
739 | 740 | label_more_than_ago: il y a plus de |
|
740 | 741 | label_ago: il y a |
|
741 | 742 | label_contains: contient |
|
742 | 743 | label_not_contains: ne contient pas |
|
743 | 744 | label_any_issues_in_project: une demande du projet |
|
744 | 745 | label_any_issues_not_in_project: une demande hors du projet |
|
745 | 746 | label_no_issues_in_project: aucune demande du projet |
|
746 | 747 | label_any_open_issues: une demande ouverte |
|
747 | 748 | label_no_open_issues: aucune demande ouverte |
|
748 | 749 | label_day_plural: jours |
|
749 | 750 | label_repository: DΓ©pΓ΄t |
|
750 | 751 | label_repository_new: Nouveau dΓ©pΓ΄t |
|
751 | 752 | label_repository_plural: DΓ©pΓ΄ts |
|
752 | 753 | label_browse: Parcourir |
|
753 | 754 | label_branch: Branche |
|
754 | 755 | label_tag: Tag |
|
755 | 756 | label_revision: RΓ©vision |
|
756 | 757 | label_revision_plural: RΓ©visions |
|
757 | 758 | label_revision_id: "RΓ©vision %{value}" |
|
758 | 759 | label_associated_revisions: RΓ©visions associΓ©es |
|
759 | 760 | label_added: ajoutΓ© |
|
760 | 761 | label_modified: modifiΓ© |
|
761 | 762 | label_copied: copiΓ© |
|
762 | 763 | label_renamed: renommΓ© |
|
763 | 764 | label_deleted: supprimΓ© |
|
764 | 765 | label_latest_revision: Dernière révision |
|
765 | 766 | label_latest_revision_plural: Dernières révisions |
|
766 | 767 | label_view_revisions: Voir les rΓ©visions |
|
767 | 768 | label_view_all_revisions: Voir toutes les rΓ©visions |
|
768 | 769 | label_max_size: Taille maximale |
|
769 | 770 | label_sort_highest: Remonter en premier |
|
770 | 771 | label_sort_higher: Remonter |
|
771 | 772 | label_sort_lower: Descendre |
|
772 | 773 | label_sort_lowest: Descendre en dernier |
|
773 | 774 | label_roadmap: Roadmap |
|
774 | 775 | label_roadmap_due_in: "ΓchΓ©ance dans %{value}" |
|
775 | 776 | label_roadmap_overdue: "En retard de %{value}" |
|
776 | 777 | label_roadmap_no_issues: Aucune demande pour cette version |
|
777 | 778 | label_search: Recherche |
|
778 | 779 | label_result_plural: RΓ©sultats |
|
779 | 780 | label_all_words: Tous les mots |
|
780 | 781 | label_wiki: Wiki |
|
781 | 782 | label_wiki_edit: RΓ©vision wiki |
|
782 | 783 | label_wiki_edit_plural: RΓ©visions wiki |
|
783 | 784 | label_wiki_page: Page wiki |
|
784 | 785 | label_wiki_page_plural: Pages wiki |
|
785 | 786 | label_wiki_page_new: Nouvelle page wiki |
|
786 | 787 | label_index_by_title: Index par titre |
|
787 | 788 | label_index_by_date: Index par date |
|
788 | 789 | label_current_version: Version actuelle |
|
789 | 790 | label_preview: PrΓ©visualisation |
|
790 | 791 | label_feed_plural: Flux Atom |
|
791 | 792 | label_changes_details: DΓ©tails de tous les changements |
|
792 | 793 | label_issue_tracking: Suivi des demandes |
|
793 | 794 | label_spent_time: Temps passΓ© |
|
794 | 795 | label_total_spent_time: Temps passΓ© total |
|
795 | 796 | label_overall_spent_time: Temps passΓ© global |
|
796 | 797 | label_f_hour: "%{value} heure" |
|
797 | 798 | label_f_hour_plural: "%{value} heures" |
|
798 | 799 | label_f_hour_short: "%{value} h" |
|
799 | 800 | label_time_tracking: Suivi du temps |
|
800 | 801 | label_change_plural: Changements |
|
801 | 802 | label_statistics: Statistiques |
|
802 | 803 | label_commits_per_month: Commits par mois |
|
803 | 804 | label_commits_per_author: Commits par auteur |
|
804 | 805 | label_diff: diff |
|
805 | 806 | label_view_diff: Voir les diffΓ©rences |
|
806 | 807 | label_diff_inline: en ligne |
|
807 | 808 | label_diff_side_by_side: cΓ΄te Γ cΓ΄te |
|
808 | 809 | label_options: Options |
|
809 | 810 | label_copy_workflow_from: Copier le workflow de |
|
810 | 811 | label_permissions_report: Synthèse des permissions |
|
811 | 812 | label_watched_issues: Demandes surveillΓ©es |
|
812 | 813 | label_related_issues: Demandes liΓ©es |
|
813 | 814 | label_applied_status: Statut appliquΓ© |
|
814 | 815 | label_loading: Chargement... |
|
815 | 816 | label_relation_new: Nouvelle relation |
|
816 | 817 | label_relation_delete: Supprimer la relation |
|
817 | 818 | label_relates_to: LiΓ© Γ |
|
818 | 819 | label_duplicates: Duplique |
|
819 | 820 | label_duplicated_by: DupliquΓ© par |
|
820 | 821 | label_blocks: Bloque |
|
821 | 822 | label_blocked_by: BloquΓ© par |
|
822 | 823 | label_precedes: Précède |
|
823 | 824 | label_follows: Suit |
|
824 | 825 | label_copied_to: CopiΓ© vers |
|
825 | 826 | label_copied_from: CopiΓ© depuis |
|
826 | 827 | label_stay_logged_in: Rester connectΓ© |
|
827 | 828 | label_disabled: dΓ©sactivΓ© |
|
828 | 829 | label_show_completed_versions: Voir les versions passΓ©es |
|
829 | 830 | label_me: moi |
|
830 | 831 | label_board: Forum |
|
831 | 832 | label_board_new: Nouveau forum |
|
832 | 833 | label_board_plural: Forums |
|
833 | 834 | label_board_locked: VerrouillΓ© |
|
834 | 835 | label_board_sticky: Sticky |
|
835 | 836 | label_topic_plural: Discussions |
|
836 | 837 | label_message_plural: Messages |
|
837 | 838 | label_message_last: Dernier message |
|
838 | 839 | label_message_new: Nouveau message |
|
839 | 840 | label_message_posted: Message ajoutΓ© |
|
840 | 841 | label_reply_plural: RΓ©ponses |
|
841 | 842 | label_send_information: Envoyer les informations Γ l'utilisateur |
|
842 | 843 | label_year: AnnΓ©e |
|
843 | 844 | label_month: Mois |
|
844 | 845 | label_week: Semaine |
|
845 | 846 | label_date_from: Du |
|
846 | 847 | label_date_to: Au |
|
847 | 848 | label_language_based: BasΓ© sur la langue de l'utilisateur |
|
848 | 849 | label_sort_by: "Trier par %{value}" |
|
849 | 850 | label_send_test_email: Envoyer un email de test |
|
850 | 851 | label_feeds_access_key: Clé d'accès Atom |
|
851 | 852 | label_missing_feeds_access_key: Clé d'accès Atom manquante |
|
852 | 853 | label_feeds_access_key_created_on: "Clé d'accès Atom créée il y a %{value}" |
|
853 | 854 | label_module_plural: Modules |
|
854 | 855 | label_added_time_by: "AjoutΓ© par %{author} il y a %{age}" |
|
855 | 856 | label_updated_time_by: "Mis Γ jour par %{author} il y a %{age}" |
|
856 | 857 | label_updated_time: "Mis Γ jour il y a %{value}" |
|
857 | 858 | label_jump_to_a_project: Aller Γ un projet... |
|
858 | 859 | label_file_plural: Fichiers |
|
859 | 860 | label_changeset_plural: RΓ©visions |
|
860 | 861 | label_default_columns: Colonnes par dΓ©faut |
|
861 | 862 | label_no_change_option: (Pas de changement) |
|
862 | 863 | label_bulk_edit_selected_issues: Modifier les demandes sΓ©lectionnΓ©es |
|
863 | 864 | label_bulk_edit_selected_time_entries: Modifier les temps passΓ©s sΓ©lectionnΓ©s |
|
864 | 865 | label_theme: Thème |
|
865 | 866 | label_default: DΓ©faut |
|
866 | 867 | label_search_titles_only: Uniquement dans les titres |
|
867 | 868 | label_user_mail_option_all: "Pour tous les Γ©vΓ©nements de tous mes projets" |
|
868 | 869 | label_user_mail_option_selected: "Pour tous les Γ©vΓ©nements des projets sΓ©lectionnΓ©s..." |
|
869 | 870 | label_user_mail_option_none: Aucune notification |
|
870 | 871 | label_user_mail_option_only_my_events: Seulement pour ce que je surveille |
|
871 | 872 | label_user_mail_option_only_assigned: Seulement pour ce qui m'est assignΓ© |
|
872 | 873 | label_user_mail_option_only_owner: Seulement pour ce que j'ai créé |
|
873 | 874 | label_user_mail_no_self_notified: "Je ne veux pas Γͺtre notifiΓ© des changements que j'effectue" |
|
874 | 875 | label_registration_activation_by_email: activation du compte par email |
|
875 | 876 | label_registration_manual_activation: activation manuelle du compte |
|
876 | 877 | label_registration_automatic_activation: activation automatique du compte |
|
877 | 878 | label_display_per_page: "Par page : %{value}" |
|
878 | 879 | label_age: Γge |
|
879 | 880 | label_change_properties: Changer les propriΓ©tΓ©s |
|
880 | 881 | label_general: GΓ©nΓ©ral |
|
881 | 882 | label_more: Plus |
|
882 | 883 | label_scm: SCM |
|
883 | 884 | label_plugins: Plugins |
|
884 | 885 | label_ldap_authentication: Authentification LDAP |
|
885 | 886 | label_downloads_abbr: D/L |
|
886 | 887 | label_optional_description: Description facultative |
|
887 | 888 | label_add_another_file: Ajouter un autre fichier |
|
888 | 889 | label_preferences: PrΓ©fΓ©rences |
|
889 | 890 | label_chronological_order: Dans l'ordre chronologique |
|
890 | 891 | label_reverse_chronological_order: Dans l'ordre chronologique inverse |
|
891 | 892 | label_planning: Planning |
|
892 | 893 | label_incoming_emails: Emails entrants |
|
893 | 894 | label_generate_key: GΓ©nΓ©rer une clΓ© |
|
894 | 895 | label_issue_watchers: Observateurs |
|
895 | 896 | label_example: Exemple |
|
896 | 897 | label_display: Affichage |
|
897 | 898 | label_sort: Tri |
|
898 | 899 | label_ascending: Croissant |
|
899 | 900 | label_descending: DΓ©croissant |
|
900 | 901 | label_date_from_to: Du %{start} au %{end} |
|
901 | 902 | label_wiki_content_added: Page wiki ajoutΓ©e |
|
902 | 903 | label_wiki_content_updated: Page wiki mise Γ jour |
|
903 | 904 | label_group: Groupe |
|
904 | 905 | label_group_plural: Groupes |
|
905 | 906 | label_group_new: Nouveau groupe |
|
906 | 907 | label_group_anonymous: Utilisateurs anonymes |
|
907 | 908 | label_group_non_member: Utilisateurs non membres |
|
908 | 909 | label_time_entry_plural: Temps passΓ© |
|
909 | 910 | label_version_sharing_none: Non partagΓ© |
|
910 | 911 | label_version_sharing_descendants: Avec les sous-projets |
|
911 | 912 | label_version_sharing_hierarchy: Avec toute la hiΓ©rarchie |
|
912 | 913 | label_version_sharing_tree: Avec tout l'arbre |
|
913 | 914 | label_version_sharing_system: Avec tous les projets |
|
914 | 915 | label_update_issue_done_ratios: Mettre Γ jour l'avancement des demandes |
|
915 | 916 | label_copy_source: Source |
|
916 | 917 | label_copy_target: Cible |
|
917 | 918 | label_copy_same_as_target: Comme la cible |
|
918 | 919 | label_display_used_statuses_only: N'afficher que les statuts utilisΓ©s dans ce tracker |
|
919 | 920 | label_api_access_key: Clé d'accès API |
|
920 | 921 | label_missing_api_access_key: Clé d'accès API manquante |
|
921 | 922 | label_api_access_key_created_on: Clé d'accès API créée il y a %{value} |
|
922 | 923 | label_profile: Profil |
|
923 | 924 | label_subtask_plural: Sous-tΓ’ches |
|
924 | 925 | label_project_copy_notifications: Envoyer les notifications durant la copie du projet |
|
925 | 926 | label_principal_search: "Rechercher un utilisateur ou un groupe :" |
|
926 | 927 | label_user_search: "Rechercher un utilisateur :" |
|
927 | 928 | label_additional_workflow_transitions_for_author: Autorisations supplémentaires lorsque l'utilisateur a créé la demande |
|
928 | 929 | label_additional_workflow_transitions_for_assignee: Autorisations supplΓ©mentaires lorsque la demande est assignΓ©e Γ l'utilisateur |
|
929 | 930 | label_issues_visibility_all: Toutes les demandes |
|
930 | 931 | label_issues_visibility_public: Toutes les demandes non privΓ©es |
|
931 | 932 | label_issues_visibility_own: Demandes créées par ou assignées à l'utilisateur |
|
932 | 933 | label_git_report_last_commit: Afficher le dernier commit des fichiers et rΓ©pertoires |
|
933 | 934 | label_parent_revision: Parent |
|
934 | 935 | label_child_revision: Enfant |
|
935 | 936 | label_export_options: Options d'exportation %{export_format} |
|
936 | 937 | label_copy_attachments: Copier les fichiers |
|
937 | 938 | label_copy_subtasks: Copier les sous-tΓ’ches |
|
938 | 939 | label_item_position: "%{position} sur %{count}" |
|
939 | 940 | label_completed_versions: Versions passΓ©es |
|
940 | 941 | label_search_for_watchers: Rechercher des observateurs |
|
941 | 942 | label_session_expiration: Expiration des sessions |
|
942 | 943 | label_show_closed_projects: Voir les projets fermΓ©s |
|
943 | 944 | label_status_transitions: Changements de statut |
|
944 | 945 | label_fields_permissions: Permissions sur les champs |
|
945 | 946 | label_readonly: Lecture |
|
946 | 947 | label_required: Obligatoire |
|
947 | 948 | label_hidden: CachΓ© |
|
948 | 949 | label_attribute_of_project: "%{name} du projet" |
|
949 | 950 | label_attribute_of_issue: "%{name} de la demande" |
|
950 | 951 | label_attribute_of_author: "%{name} de l'auteur" |
|
951 | 952 | label_attribute_of_assigned_to: "%{name} de l'assignΓ©" |
|
952 | 953 | label_attribute_of_user: "%{name} de l'utilisateur" |
|
953 | 954 | label_attribute_of_fixed_version: "%{name} de la version cible" |
|
954 | 955 | label_cross_project_descendants: Avec les sous-projets |
|
955 | 956 | label_cross_project_tree: Avec tout l'arbre |
|
956 | 957 | label_cross_project_hierarchy: Avec toute la hiΓ©rarchie |
|
957 | 958 | label_cross_project_system: Avec tous les projets |
|
958 | 959 | label_gantt_progress_line: Ligne de progression |
|
959 | 960 | label_visibility_private: par moi uniquement |
|
960 | 961 | label_visibility_roles: par ces rΓ΄les uniquement |
|
961 | 962 | label_visibility_public: par tout le monde |
|
962 | 963 | label_link: Lien |
|
963 | 964 | label_only: seulement |
|
964 | 965 | label_drop_down_list: liste dΓ©roulante |
|
965 | 966 | label_checkboxes: cases Γ cocher |
|
966 | 967 | label_radio_buttons: boutons radio |
|
967 | 968 | label_link_values_to: Lier les valeurs vers l'URL |
|
968 | 969 | label_custom_field_select_type: Selectionner le type d'objet auquel attacher le champ personnalisΓ© |
|
969 | 970 | label_check_for_updates: VΓ©rifier les mises Γ jour |
|
970 | 971 | label_latest_compatible_version: Dernière version compatible |
|
971 | 972 | label_unknown_plugin: Plugin inconnu |
|
972 | 973 | label_add_projects: Ajouter des projets |
|
973 | 974 | label_users_visibility_all: Tous les utilisateurs actifs |
|
974 | 975 | label_users_visibility_members_of_visible_projects: Membres des projets visibles |
|
975 | 976 | label_edit_attachments: Modifier les fichiers attachΓ©s |
|
976 | 977 | label_link_copied_issue: Lier la demande copiΓ©e |
|
977 | 978 | label_ask: Demander |
|
978 | 979 | label_search_attachments_yes: Rechercher les noms et descriptions de fichiers |
|
979 | 980 | label_search_attachments_no: Ne pas rechercher les fichiers |
|
980 | 981 | label_search_attachments_only: Rechercher les fichiers uniquement |
|
981 | 982 | label_search_open_issues_only: Demandes ouvertes uniquement |
|
982 | 983 | label_email_address_plural: Emails |
|
983 | 984 | label_email_address_add: Ajouter une adresse email |
|
984 | 985 | label_enable_notifications: Activer les notifications |
|
985 | 986 | label_disable_notifications: DΓ©sactiver les notifications |
|
986 | 987 | label_blank_value: non renseignΓ© |
|
987 | 988 | label_parent_task_attributes: Attributs des tΓ’ches parentes |
|
988 | 989 | label_time_entries_visibility_all: Tous les temps passΓ©s |
|
989 | 990 | label_time_entries_visibility_own: Ses propres temps passΓ©s |
|
990 | 991 | label_member_management: Gestion des membres |
|
991 | 992 | label_member_management_all_roles: Tous les rΓ΄les |
|
992 | 993 | label_member_management_selected_roles_only: Ces rΓ΄les uniquement |
|
993 | 994 | label_import_issues: Importer des demandes |
|
994 | 995 | label_select_file_to_import: SΓ©lectionner le fichier Γ importer |
|
995 | 996 | label_fields_separator: SΓ©parateur de champs |
|
996 | 997 | label_fields_wrapper: DΓ©limiteur de texte |
|
997 | 998 | label_encoding: Encodage |
|
998 | 999 | label_comma_char: Virgule |
|
999 | 1000 | label_semi_colon_char: Point virgule |
|
1000 | 1001 | label_quote_char: Apostrophe |
|
1001 | 1002 | label_double_quote_char: Double apostrophe |
|
1002 | 1003 | label_fields_mapping: Correspondance des champs |
|
1003 | 1004 | label_file_content_preview: AperΓ§u du contenu du fichier |
|
1004 | 1005 | label_create_missing_values: CrΓ©er les valeurs manquantes |
|
1005 | 1006 | label_api: API |
|
1006 | 1007 | label_field_format_enumeration: Liste clΓ©/valeur |
|
1007 | 1008 | label_default_values_for_new_users: Valeurs par dΓ©faut pour les nouveaux utilisateurs |
|
1008 | 1009 | label_relations: Relations |
|
1009 | 1010 | label_new_project_issue_tab_enabled: Afficher l'onglet "Nouvelle demande" |
|
1010 | 1011 | label_new_object_tab_enabled: Afficher le menu dΓ©roulant "+" |
|
1011 | 1012 | label_font_default: Police par dΓ©faut |
|
1012 | 1013 | label_font_monospace: Police non proportionnelle |
|
1013 | 1014 | label_font_proportional: Police proportionnelle |
|
1014 | 1015 | |
|
1015 | 1016 | button_login: Connexion |
|
1016 | 1017 | button_submit: Soumettre |
|
1017 | 1018 | button_save: Sauvegarder |
|
1018 | 1019 | button_check_all: Tout cocher |
|
1019 | 1020 | button_uncheck_all: Tout dΓ©cocher |
|
1020 | 1021 | button_collapse_all: Plier tout |
|
1021 | 1022 | button_expand_all: DΓ©plier tout |
|
1022 | 1023 | button_delete: Supprimer |
|
1023 | 1024 | button_create: CrΓ©er |
|
1024 | 1025 | button_create_and_continue: CrΓ©er et continuer |
|
1025 | 1026 | button_test: Tester |
|
1026 | 1027 | button_edit: Modifier |
|
1027 | 1028 | button_edit_associated_wikipage: "Modifier la page wiki associΓ©e: %{page_title}" |
|
1028 | 1029 | button_add: Ajouter |
|
1029 | 1030 | button_change: Changer |
|
1030 | 1031 | button_apply: Appliquer |
|
1031 | 1032 | button_clear: Effacer |
|
1032 | 1033 | button_lock: Verrouiller |
|
1033 | 1034 | button_unlock: DΓ©verrouiller |
|
1034 | 1035 | button_download: TΓ©lΓ©charger |
|
1035 | 1036 | button_list: Lister |
|
1036 | 1037 | button_view: Voir |
|
1037 | 1038 | button_move: DΓ©placer |
|
1038 | 1039 | button_move_and_follow: DΓ©placer et suivre |
|
1039 | 1040 | button_back: Retour |
|
1040 | 1041 | button_cancel: Annuler |
|
1041 | 1042 | button_activate: Activer |
|
1042 | 1043 | button_sort: Trier |
|
1043 | 1044 | button_log_time: Saisir temps |
|
1044 | 1045 | button_rollback: Revenir Γ cette version |
|
1045 | 1046 | button_watch: Surveiller |
|
1046 | 1047 | button_unwatch: Ne plus surveiller |
|
1047 | 1048 | button_reply: RΓ©pondre |
|
1048 | 1049 | button_archive: Archiver |
|
1049 | 1050 | button_unarchive: DΓ©sarchiver |
|
1050 | 1051 | button_reset: RΓ©initialiser |
|
1051 | 1052 | button_rename: Renommer |
|
1052 | 1053 | button_change_password: Changer de mot de passe |
|
1053 | 1054 | button_copy: Copier |
|
1054 | 1055 | button_copy_and_follow: Copier et suivre |
|
1055 | 1056 | button_annotate: Annoter |
|
1056 | 1057 | button_update: Mettre Γ jour |
|
1057 | 1058 | button_configure: Configurer |
|
1058 | 1059 | button_quote: Citer |
|
1059 | 1060 | button_duplicate: Dupliquer |
|
1060 | 1061 | button_show: Afficher |
|
1061 | 1062 | button_hide: Cacher |
|
1062 | 1063 | button_edit_section: Modifier cette section |
|
1063 | 1064 | button_export: Exporter |
|
1064 | 1065 | button_delete_my_account: Supprimer mon compte |
|
1065 | 1066 | button_close: Fermer |
|
1066 | 1067 | button_reopen: RΓ©ouvrir |
|
1067 | 1068 | button_import: Importer |
|
1068 | 1069 | button_filter: Filtrer |
|
1069 | 1070 | |
|
1070 | 1071 | status_active: actif |
|
1071 | 1072 | status_registered: enregistrΓ© |
|
1072 | 1073 | status_locked: verrouillΓ© |
|
1073 | 1074 | |
|
1074 | 1075 | project_status_active: actif |
|
1075 | 1076 | project_status_closed: fermΓ© |
|
1076 | 1077 | project_status_archived: archivΓ© |
|
1077 | 1078 | |
|
1078 | 1079 | version_status_open: ouvert |
|
1079 | 1080 | version_status_locked: verrouillΓ© |
|
1080 | 1081 | version_status_closed: fermΓ© |
|
1081 | 1082 | |
|
1082 | 1083 | field_active: Actif |
|
1083 | 1084 | |
|
1084 | 1085 | text_select_mail_notifications: Actions pour lesquelles une notification par e-mail est envoyΓ©e |
|
1085 | 1086 | text_regexp_info: ex. ^[A-Z0-9]+$ |
|
1086 | 1087 | text_min_max_length_info: 0 pour aucune restriction |
|
1087 | 1088 | text_project_destroy_confirmation: Γtes-vous sΓ»r de vouloir supprimer ce projet et toutes ses donnΓ©es ? |
|
1088 | 1089 | text_subprojects_destroy_warning: "Ses sous-projets : %{value} seront Γ©galement supprimΓ©s." |
|
1089 | 1090 | text_workflow_edit: SΓ©lectionner un tracker et un rΓ΄le pour Γ©diter le workflow |
|
1090 | 1091 | text_are_you_sure: Γtes-vous sΓ»r ? |
|
1091 | 1092 | text_journal_changed: "%{label} changΓ© de %{old} Γ %{new}" |
|
1092 | 1093 | text_journal_changed_no_detail: "%{label} mis Γ jour" |
|
1093 | 1094 | text_journal_set_to: "%{label} mis Γ %{value}" |
|
1094 | 1095 | text_journal_deleted: "%{label} %{old} supprimΓ©" |
|
1095 | 1096 | text_journal_added: "%{label} %{value} ajoutΓ©" |
|
1096 | 1097 | text_tip_issue_begin_day: tΓ’che commenΓ§ant ce jour |
|
1097 | 1098 | text_tip_issue_end_day: tΓ’che finissant ce jour |
|
1098 | 1099 | text_tip_issue_begin_end_day: tΓ’che commenΓ§ant et finissant ce jour |
|
1099 | 1100 | 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Γ©.' |
|
1100 | 1101 | text_caracters_maximum: "%{count} caractères maximum." |
|
1101 | 1102 | text_caracters_minimum: "%{count} caractères minimum." |
|
1102 | 1103 | text_length_between: "Longueur comprise entre %{min} et %{max} caractères." |
|
1103 | 1104 | text_tracker_no_workflow: Aucun worflow n'est dΓ©fini pour ce tracker |
|
1104 | 1105 | text_unallowed_characters: Caractères non autorisés |
|
1105 | 1106 | text_comma_separated: Plusieurs valeurs possibles (sΓ©parΓ©es par des virgules). |
|
1106 | 1107 | text_line_separated: Plusieurs valeurs possibles (une valeur par ligne). |
|
1107 | 1108 | text_issues_ref_in_commit_messages: RΓ©fΓ©rencement et rΓ©solution des demandes dans les commentaires de commits |
|
1108 | 1109 | text_issue_added: "La demande %{id} a Γ©tΓ© soumise par %{author}." |
|
1109 | 1110 | text_issue_updated: "La demande %{id} a Γ©tΓ© mise Γ jour par %{author}." |
|
1110 | 1111 | text_wiki_destroy_confirmation: Etes-vous sΓ»r de vouloir supprimer ce wiki et tout son contenu ? |
|
1111 | 1112 | text_issue_category_destroy_question: "%{count} demandes sont affectΓ©es Γ cette catΓ©gorie. Que voulez-vous faire ?" |
|
1112 | 1113 | text_issue_category_destroy_assignments: N'affecter les demandes Γ aucune autre catΓ©gorie |
|
1113 | 1114 | text_issue_category_reassign_to: RΓ©affecter les demandes Γ cette catΓ©gorie |
|
1114 | 1115 | 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)." |
|
1115 | 1116 | 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Γ©." |
|
1116 | 1117 | text_load_default_configuration: Charger le paramΓ©trage par dΓ©faut |
|
1117 | 1118 | text_status_changed_by_changeset: "AppliquΓ© par commit %{value}." |
|
1118 | 1119 | text_time_logged_by_changeset: "AppliquΓ© par commit %{value}" |
|
1119 | 1120 | text_issues_destroy_confirmation: 'Γtes-vous sΓ»r de vouloir supprimer la ou les demandes(s) selectionnΓ©e(s) ?' |
|
1120 | 1121 | text_issues_destroy_descendants_confirmation: "Cela entrainera Γ©galement la suppression de %{count} sous-tΓ’che(s)." |
|
1121 | 1122 | text_time_entries_destroy_confirmation: "Etes-vous sΓ»r de vouloir supprimer les temps passΓ©s sΓ©lectionnΓ©s ?" |
|
1122 | 1123 | text_select_project_modules: 'SΓ©lectionner les modules Γ activer pour ce projet :' |
|
1123 | 1124 | text_default_administrator_account_changed: Compte administrateur par dΓ©faut changΓ© |
|
1124 | 1125 | text_file_repository_writable: RΓ©pertoire de stockage des fichiers accessible en Γ©criture |
|
1125 | 1126 | text_plugin_assets_writable: RΓ©pertoire public des plugins accessible en Γ©criture |
|
1126 | 1127 | text_rmagick_available: Bibliothèque RMagick présente (optionnelle) |
|
1127 | 1128 | text_convert_available: Binaire convert de ImageMagick prΓ©sent (optionel) |
|
1128 | 1129 | text_destroy_time_entries_question: "%{hours} heures ont Γ©tΓ© enregistrΓ©es sur les demandes Γ supprimer. Que voulez-vous faire ?" |
|
1129 | 1130 | text_destroy_time_entries: Supprimer les heures |
|
1130 | 1131 | text_assign_time_entries_to_project: Reporter les heures sur le projet |
|
1131 | 1132 | text_reassign_time_entries: 'Reporter les heures sur cette demande:' |
|
1132 | 1133 | text_user_wrote: "%{value} a Γ©crit :" |
|
1133 | 1134 | text_enumeration_destroy_question: "La valeur Β« %{name} Β» est affectΓ©e Γ %{count} objet(s)." |
|
1134 | 1135 | text_enumeration_category_reassign_to: 'RΓ©affecter les objets Γ cette valeur:' |
|
1135 | 1136 | 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." |
|
1136 | 1137 | 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." |
|
1137 | 1138 | text_diff_truncated: '... Ce diffΓ©rentiel a Γ©tΓ© tronquΓ© car il excΓ¨de la taille maximale pouvant Γͺtre affichΓ©e.' |
|
1138 | 1139 | text_custom_field_possible_values_info: 'Une ligne par valeur' |
|
1139 | 1140 | text_wiki_page_destroy_question: "Cette page possède %{descendants} sous-page(s) et descendante(s). Que voulez-vous faire ?" |
|
1140 | 1141 | text_wiki_page_nullify_children: "Conserver les sous-pages en tant que pages racines" |
|
1141 | 1142 | text_wiki_page_destroy_children: "Supprimer les sous-pages et toutes leurs descedantes" |
|
1142 | 1143 | text_wiki_page_reassign_children: "RΓ©affecter les sous-pages Γ cette page" |
|
1143 | 1144 | 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 ?" |
|
1144 | 1145 | text_zoom_in: Zoom avant |
|
1145 | 1146 | text_zoom_out: Zoom arrière |
|
1146 | 1147 | text_warn_on_leaving_unsaved: "Cette page contient du texte non sauvegardΓ© qui sera perdu si vous quittez la page." |
|
1147 | 1148 | text_scm_path_encoding_note: "DΓ©faut : UTF-8" |
|
1148 | 1149 | text_subversion_repository_note: "Exemples (en fonction des protocoles supportΓ©s) : file:///, http://, https://, svn://, svn+[tunnelscheme]://" |
|
1149 | 1150 | text_git_repository_note: "Chemin vers un dΓ©pΓ΄t vide et local (exemples : /gitrepo, c:\\gitrepo)" |
|
1150 | 1151 | text_mercurial_repository_note: "Chemin vers un dΓ©pΓ΄t local (exemples : /hgrepo, c:\\hgrepo)" |
|
1151 | 1152 | text_scm_command: Commande |
|
1152 | 1153 | text_scm_command_version: Version |
|
1153 | 1154 | text_scm_config: Vous pouvez configurer les commandes des SCM dans config/configuration.yml. Redémarrer l'application après modification. |
|
1154 | 1155 | text_scm_command_not_available: Ce SCM n'est pas disponible. Vérifier les paramètres dans la section administration. |
|
1155 | 1156 | 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)" |
|
1156 | 1157 | text_issue_conflict_resolution_add_notes: "Ajouter mes notes et ignorer mes autres changements" |
|
1157 | 1158 | text_issue_conflict_resolution_cancel: "Annuler ma mise Γ jour et rΓ©afficher %{link}" |
|
1158 | 1159 | text_account_destroy_confirmation: "Γtes-vous sΓ»r de vouloir continuer ?\nVotre compte sera dΓ©finitivement supprimΓ©, sans aucune possibilitΓ© de le rΓ©activer." |
|
1159 | 1160 | 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." |
|
1160 | 1161 | text_project_closed: Ce projet est fermΓ© et accessible en lecture seule. |
|
1161 | 1162 | 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." |
|
1162 | 1163 | |
|
1163 | 1164 | default_role_manager: Manager |
|
1164 | 1165 | default_role_developer: DΓ©veloppeur |
|
1165 | 1166 | default_role_reporter: Rapporteur |
|
1166 | 1167 | default_tracker_bug: Anomalie |
|
1167 | 1168 | default_tracker_feature: Evolution |
|
1168 | 1169 | default_tracker_support: Assistance |
|
1169 | 1170 | default_issue_status_new: Nouveau |
|
1170 | 1171 | default_issue_status_in_progress: En cours |
|
1171 | 1172 | default_issue_status_resolved: RΓ©solu |
|
1172 | 1173 | default_issue_status_feedback: Commentaire |
|
1173 | 1174 | default_issue_status_closed: FermΓ© |
|
1174 | 1175 | default_issue_status_rejected: RejetΓ© |
|
1175 | 1176 | default_doc_category_user: Documentation utilisateur |
|
1176 | 1177 | default_doc_category_tech: Documentation technique |
|
1177 | 1178 | default_priority_low: Bas |
|
1178 | 1179 | default_priority_normal: Normal |
|
1179 | 1180 | default_priority_high: Haut |
|
1180 | 1181 | default_priority_urgent: Urgent |
|
1181 | 1182 | default_priority_immediate: ImmΓ©diat |
|
1182 | 1183 | default_activity_design: Conception |
|
1183 | 1184 | default_activity_development: DΓ©veloppement |
|
1184 | 1185 | |
|
1185 | 1186 | enumeration_issue_priorities: PrioritΓ©s des demandes |
|
1186 | 1187 | enumeration_doc_categories: CatΓ©gories des documents |
|
1187 | 1188 | enumeration_activities: ActivitΓ©s (suivi du temps) |
|
1188 | 1189 | enumeration_system_activity: Activité système |
|
1189 | 1190 | description_filter: Filtre |
|
1190 | 1191 | description_search: Champ de recherche |
|
1191 | 1192 | description_choose_project: Projets |
|
1192 | 1193 | description_project_scope: Périmètre de recherche |
|
1193 | 1194 | description_notes: Notes |
|
1194 | 1195 | description_message_content: Contenu du message |
|
1195 | 1196 | description_query_sort_criteria_attribute: Critère de tri |
|
1196 | 1197 | description_query_sort_criteria_direction: Ordre de tri |
|
1197 | 1198 | description_user_mail_notification: Option de notification |
|
1198 | 1199 | description_available_columns: Colonnes disponibles |
|
1199 | 1200 | description_selected_columns: Colonnes sΓ©lectionnΓ©es |
|
1200 | 1201 | description_all_columns: Toutes les colonnes |
|
1201 | 1202 | description_issue_category_reassign: Choisir une catΓ©gorie |
|
1202 | 1203 | description_wiki_subpages_reassign: Choisir une nouvelle page parent |
|
1203 | 1204 | description_date_range_list: Choisir une pΓ©riode prΓ©dΓ©finie |
|
1204 | 1205 | description_date_range_interval: Choisir une pΓ©riode |
|
1205 | 1206 | description_date_from: Date de dΓ©but |
|
1206 | 1207 | description_date_to: Date de fin |
|
1207 | 1208 | 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Γ©.' |
|
1208 | 1209 | label_parent_task_attributes_derived: CalculΓ© Γ partir des sous-tΓ’ches |
|
1209 | 1210 | label_parent_task_attributes_independent: IndΓ©pendent des sous-tΓ’ches |
|
1210 | 1211 | mail_subject_security_notification: Notification de sΓ©curitΓ© |
|
1211 | 1212 | mail_body_security_notification_change: ! '%{field} modifiΓ©(e).' |
|
1212 | 1213 | mail_body_security_notification_change_to: ! '%{field} changΓ©(e) en %{value}.' |
|
1213 | 1214 | mail_body_security_notification_add: ! '%{field} %{value} ajoutΓ©(e).' |
|
1214 | 1215 | mail_body_security_notification_remove: ! '%{field} %{value} supprimΓ©(e).' |
|
1215 | 1216 | mail_body_security_notification_notify_enabled: Les notifications ont Γ©tΓ© activΓ©es pour l'adresse %{value} |
|
1216 | 1217 | mail_body_security_notification_notify_disabled: Les notifications ont Γ©tΓ© dΓ©sactivΓ©es pour l'adresse %{value} |
|
1217 | 1218 | field_remote_ip: Adresse IP |
|
1218 | 1219 | label_no_preview: No preview available |
@@ -1,279 +1,281 | |||
|
1 | 1 | # Redmine - project management software |
|
2 | 2 | # Copyright (C) 2006-2016 Jean-Philippe Lang |
|
3 | 3 | # |
|
4 | 4 | # This program is free software; you can redistribute it and/or |
|
5 | 5 | # modify it under the terms of the GNU General Public License |
|
6 | 6 | # as published by the Free Software Foundation; either version 2 |
|
7 | 7 | # of the License, or (at your option) any later version. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU General Public License |
|
15 | 15 | # along with this program; if not, write to the Free Software |
|
16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | 17 | |
|
18 | 18 | |
|
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 | security_notifications: 1 |
|
31 | 31 | self_registration: |
|
32 | 32 | default: '2' |
|
33 | 33 | security_notifications: 1 |
|
34 | 34 | lost_password: |
|
35 | 35 | default: 1 |
|
36 | 36 | security_notifications: 1 |
|
37 | 37 | unsubscribe: |
|
38 | 38 | default: 1 |
|
39 | 39 | password_min_length: |
|
40 | 40 | format: int |
|
41 | 41 | default: 8 |
|
42 | 42 | security_notifications: 1 |
|
43 | 43 | # Maximum password age in days |
|
44 | 44 | password_max_age: |
|
45 | 45 | format: int |
|
46 | 46 | default: 0 |
|
47 | 47 | security_notifications: 1 |
|
48 | 48 | # Maximum number of additional email addresses per user |
|
49 | 49 | max_additional_emails: |
|
50 | 50 | format: int |
|
51 | 51 | default: 5 |
|
52 | 52 | # Maximum lifetime of user sessions in minutes |
|
53 | 53 | session_lifetime: |
|
54 | 54 | format: int |
|
55 | 55 | default: 0 |
|
56 | 56 | security_notifications: 1 |
|
57 | 57 | # User session timeout in minutes |
|
58 | 58 | session_timeout: |
|
59 | 59 | format: int |
|
60 | 60 | default: 0 |
|
61 | 61 | security_notifications: 1 |
|
62 | 62 | attachment_max_size: |
|
63 | 63 | format: int |
|
64 | 64 | default: 5120 |
|
65 | 65 | attachment_extensions_allowed: |
|
66 | 66 | default: |
|
67 | 67 | attachment_extensions_denied: |
|
68 | 68 | default: |
|
69 | 69 | issues_export_limit: |
|
70 | 70 | format: int |
|
71 | 71 | default: 500 |
|
72 | 72 | activity_days_default: |
|
73 | 73 | format: int |
|
74 | 74 | default: 30 |
|
75 | 75 | per_page_options: |
|
76 | 76 | default: '25,50,100' |
|
77 | 77 | search_results_per_page: |
|
78 | 78 | default: 10 |
|
79 | 79 | mail_from: |
|
80 | 80 | default: redmine@example.net |
|
81 | 81 | bcc_recipients: |
|
82 | 82 | default: 1 |
|
83 | 83 | plain_text_mail: |
|
84 | 84 | default: 0 |
|
85 | 85 | text_formatting: |
|
86 | 86 | default: textile |
|
87 | 87 | cache_formatted_text: |
|
88 | 88 | default: 0 |
|
89 | 89 | wiki_compression: |
|
90 | 90 | default: "" |
|
91 | 91 | default_language: |
|
92 | 92 | default: en |
|
93 | 93 | force_default_language_for_anonymous: |
|
94 | 94 | default: 0 |
|
95 | 95 | force_default_language_for_loggedin: |
|
96 | 96 | default: 0 |
|
97 | 97 | host_name: |
|
98 | 98 | default: localhost:3000 |
|
99 | 99 | protocol: |
|
100 | 100 | default: http |
|
101 | 101 | security_notifications: 1 |
|
102 | 102 | feeds_limit: |
|
103 | 103 | format: int |
|
104 | 104 | default: 15 |
|
105 | 105 | gantt_items_limit: |
|
106 | 106 | format: int |
|
107 | 107 | default: 500 |
|
108 | 108 | # Maximum size of files that can be displayed |
|
109 | 109 | # inline through the file viewer (in KB) |
|
110 | 110 | file_max_size_displayed: |
|
111 | 111 | format: int |
|
112 | 112 | default: 512 |
|
113 | 113 | diff_max_lines_displayed: |
|
114 | 114 | format: int |
|
115 | 115 | default: 1500 |
|
116 | 116 | enabled_scm: |
|
117 | 117 | serialized: true |
|
118 | 118 | default: |
|
119 | 119 | - Subversion |
|
120 | 120 | - Darcs |
|
121 | 121 | - Mercurial |
|
122 | 122 | - Cvs |
|
123 | 123 | - Bazaar |
|
124 | 124 | - Git |
|
125 | 125 | security_notifications: 1 |
|
126 | 126 | autofetch_changesets: |
|
127 | 127 | default: 1 |
|
128 | 128 | sys_api_enabled: |
|
129 | 129 | default: 0 |
|
130 | 130 | security_notifications: 1 |
|
131 | 131 | sys_api_key: |
|
132 | 132 | default: '' |
|
133 | 133 | security_notifications: 1 |
|
134 | 134 | commit_cross_project_ref: |
|
135 | 135 | default: 0 |
|
136 | 136 | commit_ref_keywords: |
|
137 | 137 | default: 'refs,references,IssueID' |
|
138 | 138 | commit_update_keywords: |
|
139 | 139 | serialized: true |
|
140 | 140 | default: [] |
|
141 | 141 | commit_logtime_enabled: |
|
142 | 142 | default: 0 |
|
143 | 143 | commit_logtime_activity_id: |
|
144 | 144 | format: int |
|
145 | 145 | default: 0 |
|
146 | 146 | # autologin duration in days |
|
147 | 147 | # 0 means autologin is disabled |
|
148 | 148 | autologin: |
|
149 | 149 | format: int |
|
150 | 150 | default: 0 |
|
151 | 151 | # date format |
|
152 | 152 | date_format: |
|
153 | 153 | default: '' |
|
154 | 154 | time_format: |
|
155 | 155 | default: '' |
|
156 | timespan_format: | |
|
157 | default: 'decimal' | |
|
156 | 158 | user_format: |
|
157 | 159 | default: :firstname_lastname |
|
158 | 160 | format: symbol |
|
159 | 161 | cross_project_issue_relations: |
|
160 | 162 | default: 0 |
|
161 | 163 | # Enables subtasks to be in other projects |
|
162 | 164 | cross_project_subtasks: |
|
163 | 165 | default: 'tree' |
|
164 | 166 | parent_issue_dates: |
|
165 | 167 | default: 'derived' |
|
166 | 168 | parent_issue_priority: |
|
167 | 169 | default: 'derived' |
|
168 | 170 | parent_issue_done_ratio: |
|
169 | 171 | default: 'derived' |
|
170 | 172 | link_copied_issue: |
|
171 | 173 | default: 'ask' |
|
172 | 174 | issue_group_assignment: |
|
173 | 175 | default: 0 |
|
174 | 176 | default_issue_start_date_to_creation_date: |
|
175 | 177 | default: 1 |
|
176 | 178 | notified_events: |
|
177 | 179 | serialized: true |
|
178 | 180 | default: |
|
179 | 181 | - issue_added |
|
180 | 182 | - issue_updated |
|
181 | 183 | mail_handler_body_delimiters: |
|
182 | 184 | default: '' |
|
183 | 185 | mail_handler_excluded_filenames: |
|
184 | 186 | default: '' |
|
185 | 187 | mail_handler_api_enabled: |
|
186 | 188 | default: 0 |
|
187 | 189 | security_notifications: 1 |
|
188 | 190 | mail_handler_api_key: |
|
189 | 191 | default: |
|
190 | 192 | security_notifications: 1 |
|
191 | 193 | issue_list_default_columns: |
|
192 | 194 | serialized: true |
|
193 | 195 | default: |
|
194 | 196 | - tracker |
|
195 | 197 | - status |
|
196 | 198 | - priority |
|
197 | 199 | - subject |
|
198 | 200 | - assigned_to |
|
199 | 201 | - updated_on |
|
200 | 202 | issue_list_default_totals: |
|
201 | 203 | serialized: true |
|
202 | 204 | default: [] |
|
203 | 205 | display_subprojects_issues: |
|
204 | 206 | default: 1 |
|
205 | 207 | issue_done_ratio: |
|
206 | 208 | default: 'issue_field' |
|
207 | 209 | default_projects_public: |
|
208 | 210 | default: 1 |
|
209 | 211 | default_projects_modules: |
|
210 | 212 | serialized: true |
|
211 | 213 | default: |
|
212 | 214 | - issue_tracking |
|
213 | 215 | - time_tracking |
|
214 | 216 | - news |
|
215 | 217 | - documents |
|
216 | 218 | - files |
|
217 | 219 | - wiki |
|
218 | 220 | - repository |
|
219 | 221 | - boards |
|
220 | 222 | - calendar |
|
221 | 223 | - gantt |
|
222 | 224 | default_projects_tracker_ids: |
|
223 | 225 | serialized: true |
|
224 | 226 | default: |
|
225 | 227 | # Role given to a non-admin user who creates a project |
|
226 | 228 | new_project_user_role_id: |
|
227 | 229 | format: int |
|
228 | 230 | default: '' |
|
229 | 231 | sequential_project_identifiers: |
|
230 | 232 | default: 0 |
|
231 | 233 | # encodings used to convert repository files content to UTF-8 |
|
232 | 234 | # multiple values accepted, comma separated |
|
233 | 235 | default_users_hide_mail: |
|
234 | 236 | default: 1 |
|
235 | 237 | repositories_encodings: |
|
236 | 238 | default: '' |
|
237 | 239 | # encoding used to convert commit logs to UTF-8 |
|
238 | 240 | commit_logs_encoding: |
|
239 | 241 | default: 'UTF-8' |
|
240 | 242 | repository_log_display_limit: |
|
241 | 243 | format: int |
|
242 | 244 | default: 100 |
|
243 | 245 | ui_theme: |
|
244 | 246 | default: '' |
|
245 | 247 | emails_footer: |
|
246 | 248 | default: |- |
|
247 | 249 | You have received this notification because you have either subscribed to it, or are involved in it. |
|
248 | 250 | To change your notification preferences, please click here: http://hostname/my/account |
|
249 | 251 | gravatar_enabled: |
|
250 | 252 | default: 0 |
|
251 | 253 | openid: |
|
252 | 254 | default: 0 |
|
253 | 255 | security_notifications: 1 |
|
254 | 256 | gravatar_default: |
|
255 | 257 | default: '' |
|
256 | 258 | start_of_week: |
|
257 | 259 | default: '' |
|
258 | 260 | rest_api_enabled: |
|
259 | 261 | default: 0 |
|
260 | 262 | security_notifications: 1 |
|
261 | 263 | jsonp_enabled: |
|
262 | 264 | default: 0 |
|
263 | 265 | security_notifications: 1 |
|
264 | 266 | default_notification_option: |
|
265 | 267 | default: 'only_my_events' |
|
266 | 268 | emails_header: |
|
267 | 269 | default: '' |
|
268 | 270 | thumbnails_enabled: |
|
269 | 271 | default: 0 |
|
270 | 272 | thumbnails_size: |
|
271 | 273 | format: int |
|
272 | 274 | default: 100 |
|
273 | 275 | non_working_week_days: |
|
274 | 276 | serialized: true |
|
275 | 277 | default: |
|
276 | 278 | - '6' |
|
277 | 279 | - '7' |
|
278 | 280 | new_item_menu_tab: |
|
279 | 281 | default: 2 |
@@ -1,205 +1,217 | |||
|
1 | 1 | # Redmine - project management software |
|
2 | 2 | # Copyright (C) 2006-2016 Jean-Philippe Lang |
|
3 | 3 | # |
|
4 | 4 | # This program is free software; you can redistribute it and/or |
|
5 | 5 | # modify it under the terms of the GNU General Public License |
|
6 | 6 | # as published by the Free Software Foundation; either version 2 |
|
7 | 7 | # of the License, or (at your option) any later version. |
|
8 | 8 | # |
|
9 | 9 | # This program is distributed in the hope that it will be useful, |
|
10 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 | 12 | # GNU General Public License for more details. |
|
13 | 13 | # |
|
14 | 14 | # You should have received a copy of the GNU General Public License |
|
15 | 15 | # along with this program; if not, write to the Free Software |
|
16 | 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
17 | 17 | |
|
18 | 18 | module Redmine |
|
19 | 19 | module I18n |
|
20 | 20 | def self.included(base) |
|
21 | 21 | base.extend Redmine::I18n |
|
22 | 22 | end |
|
23 | 23 | |
|
24 | 24 | def l(*args) |
|
25 | 25 | case args.size |
|
26 | 26 | when 1 |
|
27 | 27 | ::I18n.t(*args) |
|
28 | 28 | when 2 |
|
29 | 29 | if args.last.is_a?(Hash) |
|
30 | 30 | ::I18n.t(*args) |
|
31 | 31 | elsif args.last.is_a?(String) |
|
32 | 32 | ::I18n.t(args.first, :value => args.last) |
|
33 | 33 | else |
|
34 | 34 | ::I18n.t(args.first, :count => args.last) |
|
35 | 35 | end |
|
36 | 36 | else |
|
37 | 37 | raise "Translation string with multiple values: #{args.first}" |
|
38 | 38 | end |
|
39 | 39 | end |
|
40 | 40 | |
|
41 | 41 | def l_or_humanize(s, options={}) |
|
42 | 42 | k = "#{options[:prefix]}#{s}".to_sym |
|
43 | 43 | ::I18n.t(k, :default => s.to_s.humanize) |
|
44 | 44 | end |
|
45 | 45 | |
|
46 | 46 | def l_hours(hours) |
|
47 | 47 | hours = hours.to_f |
|
48 |
l((hours < 2.0 ? :label_f_hour : :label_f_hour_plural), :value => ( |
|
|
48 | l((hours < 2.0 ? :label_f_hour : :label_f_hour_plural), :value => format_hours(hours)) | |
|
49 | 49 | end |
|
50 | 50 | |
|
51 | 51 | def l_hours_short(hours) |
|
52 |
l(:label_f_hour_short, :value => ( |
|
|
52 | l(:label_f_hour_short, :value => format_hours(hours.to_f)) | |
|
53 | 53 | end |
|
54 | 54 | |
|
55 | 55 | def ll(lang, str, arg=nil) |
|
56 | 56 | options = arg.is_a?(Hash) ? arg : {:value => arg} |
|
57 | 57 | locale = lang.to_s.gsub(%r{(.+)\-(.+)$}) { "#{$1}-#{$2.upcase}" } |
|
58 | 58 | ::I18n.t(str.to_s, options.merge(:locale => locale)) |
|
59 | 59 | end |
|
60 | 60 | |
|
61 | 61 | # Localizes the given args with user's language |
|
62 | 62 | def lu(user, *args) |
|
63 | 63 | lang = user.try(:language).presence || Setting.default_language |
|
64 | 64 | ll(lang, *args) |
|
65 | 65 | end |
|
66 | 66 | |
|
67 | 67 | def format_date(date) |
|
68 | 68 | return nil unless date |
|
69 | 69 | options = {} |
|
70 | 70 | options[:format] = Setting.date_format unless Setting.date_format.blank? |
|
71 | 71 | ::I18n.l(date.to_date, options) |
|
72 | 72 | end |
|
73 | 73 | |
|
74 | 74 | def format_time(time, include_date=true, user=nil) |
|
75 | 75 | return nil unless time |
|
76 | 76 | user ||= User.current |
|
77 | 77 | options = {} |
|
78 | 78 | options[:format] = (Setting.time_format.blank? ? :time : Setting.time_format) |
|
79 | 79 | time = time.to_time if time.is_a?(String) |
|
80 | 80 | zone = user.time_zone |
|
81 | 81 | local = zone ? time.in_time_zone(zone) : (time.utc? ? time.localtime : time) |
|
82 | 82 | (include_date ? "#{format_date(local)} " : "") + ::I18n.l(local, options) |
|
83 | 83 | end |
|
84 | 84 | |
|
85 | def format_hours(hours) | |
|
86 | return "" if hours.blank? | |
|
87 | ||
|
88 | if Setting.timespan_format == 'minutes' | |
|
89 | h = hours.floor | |
|
90 | m = ((hours - h) * 60).round | |
|
91 | "%d:%02d" % [ h, m ] | |
|
92 | else | |
|
93 | "%.2f" % hours.to_f | |
|
94 | end | |
|
95 | end | |
|
96 | ||
|
85 | 97 | def day_name(day) |
|
86 | 98 | ::I18n.t('date.day_names')[day % 7] |
|
87 | 99 | end |
|
88 | 100 | |
|
89 | 101 | def day_letter(day) |
|
90 | 102 | ::I18n.t('date.abbr_day_names')[day % 7].first |
|
91 | 103 | end |
|
92 | 104 | |
|
93 | 105 | def month_name(month) |
|
94 | 106 | ::I18n.t('date.month_names')[month] |
|
95 | 107 | end |
|
96 | 108 | |
|
97 | 109 | def valid_languages |
|
98 | 110 | ::I18n.available_locales |
|
99 | 111 | end |
|
100 | 112 | |
|
101 | 113 | # Returns an array of languages names and code sorted by names, example: |
|
102 | 114 | # [["Deutsch", "de"], ["English", "en"] ...] |
|
103 | 115 | # |
|
104 | 116 | # The result is cached to prevent from loading all translations files |
|
105 | 117 | # unless :cache => false option is given |
|
106 | 118 | def languages_options(options={}) |
|
107 | 119 | options = if options[:cache] == false |
|
108 | 120 | valid_languages. |
|
109 | 121 | select {|locale| ::I18n.exists?(:general_lang_name, locale)}. |
|
110 | 122 | map {|lang| [ll(lang.to_s, :general_lang_name), lang.to_s]}. |
|
111 | 123 | sort {|x,y| x.first <=> y.first } |
|
112 | 124 | else |
|
113 | 125 | ActionController::Base.cache_store.fetch "i18n/languages_options/#{Redmine::VERSION}" do |
|
114 | 126 | languages_options :cache => false |
|
115 | 127 | end |
|
116 | 128 | end |
|
117 | 129 | options.map {|name, lang| [name.force_encoding("UTF-8"), lang.force_encoding("UTF-8")]} |
|
118 | 130 | end |
|
119 | 131 | |
|
120 | 132 | def find_language(lang) |
|
121 | 133 | @@languages_lookup = valid_languages.inject({}) {|k, v| k[v.to_s.downcase] = v; k } |
|
122 | 134 | @@languages_lookup[lang.to_s.downcase] |
|
123 | 135 | end |
|
124 | 136 | |
|
125 | 137 | def set_language_if_valid(lang) |
|
126 | 138 | if l = find_language(lang) |
|
127 | 139 | ::I18n.locale = l |
|
128 | 140 | end |
|
129 | 141 | end |
|
130 | 142 | |
|
131 | 143 | def current_language |
|
132 | 144 | ::I18n.locale |
|
133 | 145 | end |
|
134 | 146 | |
|
135 | 147 | # Custom backend based on I18n::Backend::Simple with the following changes: |
|
136 | 148 | # * lazy loading of translation files |
|
137 | 149 | # * available_locales are determined by looking at translation file names |
|
138 | 150 | class Backend |
|
139 | 151 | (class << self; self; end).class_eval { public :include } |
|
140 | 152 | |
|
141 | 153 | module Implementation |
|
142 | 154 | include ::I18n::Backend::Base |
|
143 | 155 | include ::I18n::Backend::Pluralization |
|
144 | 156 | |
|
145 | 157 | # Stores translations for the given locale in memory. |
|
146 | 158 | # This uses a deep merge for the translations hash, so existing |
|
147 | 159 | # translations will be overwritten by new ones only at the deepest |
|
148 | 160 | # level of the hash. |
|
149 | 161 | def store_translations(locale, data, options = {}) |
|
150 | 162 | locale = locale.to_sym |
|
151 | 163 | translations[locale] ||= {} |
|
152 | 164 | data = data.deep_symbolize_keys |
|
153 | 165 | translations[locale].deep_merge!(data) |
|
154 | 166 | end |
|
155 | 167 | |
|
156 | 168 | # Get available locales from the translations filenames |
|
157 | 169 | def available_locales |
|
158 | 170 | @available_locales ||= ::I18n.load_path.map {|path| File.basename(path, '.*')}.uniq.sort.map(&:to_sym) |
|
159 | 171 | end |
|
160 | 172 | |
|
161 | 173 | # Clean up translations |
|
162 | 174 | def reload! |
|
163 | 175 | @translations = nil |
|
164 | 176 | @available_locales = nil |
|
165 | 177 | super |
|
166 | 178 | end |
|
167 | 179 | |
|
168 | 180 | protected |
|
169 | 181 | |
|
170 | 182 | def init_translations(locale) |
|
171 | 183 | locale = locale.to_s |
|
172 | 184 | paths = ::I18n.load_path.select {|path| File.basename(path, '.*') == locale} |
|
173 | 185 | load_translations(paths) |
|
174 | 186 | translations[locale] ||= {} |
|
175 | 187 | end |
|
176 | 188 | |
|
177 | 189 | def translations |
|
178 | 190 | @translations ||= {} |
|
179 | 191 | end |
|
180 | 192 | |
|
181 | 193 | # Looks up a translation from the translations hash. Returns nil if |
|
182 | 194 | # eiher key is nil, or locale, scope or key do not exist as a key in the |
|
183 | 195 | # nested translations hash. Splits keys or scopes containing dots |
|
184 | 196 | # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as |
|
185 | 197 | # <tt>%w(currency format)</tt>. |
|
186 | 198 | def lookup(locale, key, scope = [], options = {}) |
|
187 | 199 | init_translations(locale) unless translations.key?(locale) |
|
188 | 200 | keys = ::I18n.normalize_keys(locale, key, scope, options[:separator]) |
|
189 | 201 | |
|
190 | 202 | keys.inject(translations) do |result, _key| |
|
191 | 203 | _key = _key.to_sym |
|
192 | 204 | return nil unless result.is_a?(Hash) && result.has_key?(_key) |
|
193 | 205 | result = result[_key] |
|
194 | 206 | result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol) |
|
195 | 207 | result |
|
196 | 208 | end |
|
197 | 209 | end |
|
198 | 210 | end |
|
199 | 211 | |
|
200 | 212 | include Implementation |
|
201 | 213 | # Adds fallback to default locale for untranslated strings |
|
202 | 214 | include ::I18n::Backend::Fallbacks |
|
203 | 215 | end |
|
204 | 216 | end |
|
205 | 217 | end |
@@ -1,1547 +1,1567 | |||
|
1 | 1 | # encoding: utf-8 |
|
2 | 2 | # |
|
3 | 3 | # Redmine - project management software |
|
4 | 4 | # Copyright (C) 2006-2016 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 ApplicationHelperTest < Redmine::HelperTest |
|
23 | 23 | include Redmine::I18n |
|
24 | 24 | include ERB::Util |
|
25 | 25 | include Rails.application.routes.url_helpers |
|
26 | 26 | |
|
27 | 27 | fixtures :projects, :enabled_modules, |
|
28 | 28 | :users, :email_addresses, |
|
29 | 29 | :members, :member_roles, :roles, |
|
30 | 30 | :repositories, :changesets, |
|
31 | 31 | :projects_trackers, |
|
32 | 32 | :trackers, :issue_statuses, :issues, :versions, :documents, |
|
33 | 33 | :wikis, :wiki_pages, :wiki_contents, |
|
34 | 34 | :boards, :messages, :news, |
|
35 | 35 | :attachments, :enumerations |
|
36 | 36 | |
|
37 | 37 | def setup |
|
38 | 38 | super |
|
39 | 39 | set_tmp_attachments_directory |
|
40 | 40 | @russian_test = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82".force_encoding('UTF-8') |
|
41 | 41 | end |
|
42 | 42 | |
|
43 | 43 | test "#link_to_if_authorized for authorized user should allow using the :controller and :action for the target link" do |
|
44 | 44 | User.current = User.find_by_login('admin') |
|
45 | 45 | |
|
46 | 46 | @project = Issue.first.project # Used by helper |
|
47 | 47 | response = link_to_if_authorized('By controller/actionr', |
|
48 | 48 | {:controller => 'issues', :action => 'edit', :id => Issue.first.id}) |
|
49 | 49 | assert_match /href/, response |
|
50 | 50 | end |
|
51 | 51 | |
|
52 | 52 | test "#link_to_if_authorized for unauthorized user should display nothing if user isn't authorized" do |
|
53 | 53 | User.current = User.find_by_login('dlopper') |
|
54 | 54 | @project = Project.find('private-child') |
|
55 | 55 | issue = @project.issues.first |
|
56 | 56 | assert !issue.visible? |
|
57 | 57 | |
|
58 | 58 | response = link_to_if_authorized('Never displayed', |
|
59 | 59 | {:controller => 'issues', :action => 'show', :id => issue}) |
|
60 | 60 | assert_nil response |
|
61 | 61 | end |
|
62 | 62 | |
|
63 | 63 | def test_auto_links |
|
64 | 64 | to_test = { |
|
65 | 65 | 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>', |
|
66 | 66 | 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>', |
|
67 | 67 | 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.', |
|
68 | 68 | 'https://foo.bar.' => '<a class="external" href="https://foo.bar">https://foo.bar</a>.', |
|
69 | 69 | 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.', |
|
70 | 70 | 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).', |
|
71 | 71 | 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.', |
|
72 | 72 | 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>', |
|
73 | 73 | '(see inline link : http://www.foo.bar/Test_(foobar))' => '(see inline link : <a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>)', |
|
74 | 74 | '(see inline link : http://www.foo.bar/Test)' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>)', |
|
75 | 75 | '(see inline link : http://www.foo.bar/Test).' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>).', |
|
76 | 76 | '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)', |
|
77 | 77 | '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)', |
|
78 | 78 | '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).', |
|
79 | 79 | 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>', |
|
80 | 80 | 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&t=z&s=">http://foo.bar/page?p=1&t=z&s=</a>', |
|
81 | 81 | 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>', |
|
82 | 82 | 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>', |
|
83 | 83 | 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>', |
|
84 | 84 | 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>', |
|
85 | 85 | 'ftps://foo.bar' => '<a class="external" href="ftps://foo.bar">ftps://foo.bar</a>', |
|
86 | 86 | 'sftp://foo.bar' => '<a class="external" href="sftp://foo.bar">sftp://foo.bar</a>', |
|
87 | 87 | # two exclamation marks |
|
88 | 88 | 'http://example.net/path!602815048C7B5C20!302.html' => '<a class="external" href="http://example.net/path!602815048C7B5C20!302.html">http://example.net/path!602815048C7B5C20!302.html</a>', |
|
89 | 89 | # escaping |
|
90 | 90 | 'http://foo"bar' => '<a class="external" href="http://foo"bar">http://foo"bar</a>', |
|
91 | 91 | # wrap in angle brackets |
|
92 | 92 | '<http://foo.bar>' => '<<a class="external" href="http://foo.bar">http://foo.bar</a>>', |
|
93 | 93 | # invalid urls |
|
94 | 94 | 'http://' => 'http://', |
|
95 | 95 | 'www.' => 'www.', |
|
96 | 96 | 'test-www.bar.com' => 'test-www.bar.com', |
|
97 | 97 | } |
|
98 | 98 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
99 | 99 | end |
|
100 | 100 | |
|
101 | 101 | def test_auto_links_with_non_ascii_characters |
|
102 | 102 | to_test = { |
|
103 | 103 | "http://foo.bar/#{@russian_test}" => |
|
104 | 104 | %|<a class="external" href="http://foo.bar/#{@russian_test}">http://foo.bar/#{@russian_test}</a>| |
|
105 | 105 | } |
|
106 | 106 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
107 | 107 | end |
|
108 | 108 | |
|
109 | 109 | def test_auto_mailto |
|
110 | 110 | to_test = { |
|
111 | 111 | 'test@foo.bar' => '<a class="email" href="mailto:test@foo.bar">test@foo.bar</a>', |
|
112 | 112 | 'test@www.foo.bar' => '<a class="email" href="mailto:test@www.foo.bar">test@www.foo.bar</a>', |
|
113 | 113 | } |
|
114 | 114 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
115 | 115 | end |
|
116 | 116 | |
|
117 | 117 | def test_inline_images |
|
118 | 118 | to_test = { |
|
119 | 119 | '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />', |
|
120 | 120 | 'floating !>http://foo.bar/image.jpg!' => 'floating <span style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></span>', |
|
121 | 121 | 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />', |
|
122 | 122 | 'with style !{width:100px;height:100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height:100px;" alt="" />', |
|
123 | 123 | 'with title !http://foo.bar/image.jpg(This is a title)!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a title" alt="This is a title" />', |
|
124 | 124 | 'with title !http://foo.bar/image.jpg(This is a double-quoted "title")!' => 'with title <img src="http://foo.bar/image.jpg" title="This is a double-quoted "title"" alt="This is a double-quoted "title"" />', |
|
125 | 125 | } |
|
126 | 126 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
127 | 127 | end |
|
128 | 128 | |
|
129 | 129 | def test_inline_images_inside_tags |
|
130 | 130 | raw = <<-RAW |
|
131 | 131 | h1. !foo.png! Heading |
|
132 | 132 | |
|
133 | 133 | Centered image: |
|
134 | 134 | |
|
135 | 135 | p=. !bar.gif! |
|
136 | 136 | RAW |
|
137 | 137 | |
|
138 | 138 | assert textilizable(raw).include?('<img src="foo.png" alt="" />') |
|
139 | 139 | assert textilizable(raw).include?('<img src="bar.gif" alt="" />') |
|
140 | 140 | end |
|
141 | 141 | |
|
142 | 142 | def test_attached_images |
|
143 | 143 | to_test = { |
|
144 | 144 | 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />', |
|
145 | 145 | 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" />', |
|
146 | 146 | 'No match: !ogo.gif!' => 'No match: <img src="ogo.gif" alt="" />', |
|
147 | 147 | 'No match: !ogo.GIF!' => 'No match: <img src="ogo.GIF" alt="" />', |
|
148 | 148 | # link image |
|
149 | 149 | '!logo.gif!:http://foo.bar/' => '<a href="http://foo.bar/"><img src="/attachments/download/3/logo.gif" title="This is a logo" alt="This is a logo" /></a>', |
|
150 | 150 | } |
|
151 | 151 | attachments = Attachment.all |
|
152 | 152 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) } |
|
153 | 153 | end |
|
154 | 154 | |
|
155 | 155 | def test_attached_images_with_textile_and_non_ascii_filename |
|
156 | 156 | attachment = Attachment.generate!(:filename => 'cafΓ©.jpg') |
|
157 | 157 | with_settings :text_formatting => 'textile' do |
|
158 | 158 | assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />), |
|
159 | 159 | textilizable("!cafΓ©.jpg!)", :attachments => [attachment]) |
|
160 | 160 | end |
|
161 | 161 | end |
|
162 | 162 | |
|
163 | 163 | def test_attached_images_with_markdown_and_non_ascii_filename |
|
164 | 164 | skip unless Object.const_defined?(:Redcarpet) |
|
165 | 165 | |
|
166 | 166 | attachment = Attachment.generate!(:filename => 'cafΓ©.jpg') |
|
167 | 167 | with_settings :text_formatting => 'markdown' do |
|
168 | 168 | assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />), |
|
169 | 169 | textilizable("", :attachments => [attachment]) |
|
170 | 170 | end |
|
171 | 171 | end |
|
172 | 172 | |
|
173 | 173 | def test_attached_images_filename_extension |
|
174 | 174 | set_tmp_attachments_directory |
|
175 | 175 | a1 = Attachment.new( |
|
176 | 176 | :container => Issue.find(1), |
|
177 | 177 | :file => mock_file_with_options({:original_filename => "testtest.JPG"}), |
|
178 | 178 | :author => User.find(1)) |
|
179 | 179 | assert a1.save |
|
180 | 180 | assert_equal "testtest.JPG", a1.filename |
|
181 | 181 | assert_equal "image/jpeg", a1.content_type |
|
182 | 182 | assert a1.image? |
|
183 | 183 | |
|
184 | 184 | a2 = Attachment.new( |
|
185 | 185 | :container => Issue.find(1), |
|
186 | 186 | :file => mock_file_with_options({:original_filename => "testtest.jpeg"}), |
|
187 | 187 | :author => User.find(1)) |
|
188 | 188 | assert a2.save |
|
189 | 189 | assert_equal "testtest.jpeg", a2.filename |
|
190 | 190 | assert_equal "image/jpeg", a2.content_type |
|
191 | 191 | assert a2.image? |
|
192 | 192 | |
|
193 | 193 | a3 = Attachment.new( |
|
194 | 194 | :container => Issue.find(1), |
|
195 | 195 | :file => mock_file_with_options({:original_filename => "testtest.JPE"}), |
|
196 | 196 | :author => User.find(1)) |
|
197 | 197 | assert a3.save |
|
198 | 198 | assert_equal "testtest.JPE", a3.filename |
|
199 | 199 | assert_equal "image/jpeg", a3.content_type |
|
200 | 200 | assert a3.image? |
|
201 | 201 | |
|
202 | 202 | a4 = Attachment.new( |
|
203 | 203 | :container => Issue.find(1), |
|
204 | 204 | :file => mock_file_with_options({:original_filename => "Testtest.BMP"}), |
|
205 | 205 | :author => User.find(1)) |
|
206 | 206 | assert a4.save |
|
207 | 207 | assert_equal "Testtest.BMP", a4.filename |
|
208 | 208 | assert_equal "image/x-ms-bmp", a4.content_type |
|
209 | 209 | assert a4.image? |
|
210 | 210 | |
|
211 | 211 | to_test = { |
|
212 | 212 | 'Inline image: !testtest.jpg!' => |
|
213 | 213 | 'Inline image: <img src="/attachments/download/' + a1.id.to_s + '/testtest.JPG" alt="" />', |
|
214 | 214 | 'Inline image: !testtest.jpeg!' => |
|
215 | 215 | 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testtest.jpeg" alt="" />', |
|
216 | 216 | 'Inline image: !testtest.jpe!' => |
|
217 | 217 | 'Inline image: <img src="/attachments/download/' + a3.id.to_s + '/testtest.JPE" alt="" />', |
|
218 | 218 | 'Inline image: !testtest.bmp!' => |
|
219 | 219 | 'Inline image: <img src="/attachments/download/' + a4.id.to_s + '/Testtest.BMP" alt="" />', |
|
220 | 220 | } |
|
221 | 221 | |
|
222 | 222 | attachments = [a1, a2, a3, a4] |
|
223 | 223 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) } |
|
224 | 224 | end |
|
225 | 225 | |
|
226 | 226 | def test_attached_images_should_read_later |
|
227 | 227 | set_fixtures_attachments_directory |
|
228 | 228 | a1 = Attachment.find(16) |
|
229 | 229 | assert_equal "testfile.png", a1.filename |
|
230 | 230 | assert a1.readable? |
|
231 | 231 | assert (! a1.visible?(User.anonymous)) |
|
232 | 232 | assert a1.visible?(User.find(2)) |
|
233 | 233 | a2 = Attachment.find(17) |
|
234 | 234 | assert_equal "testfile.PNG", a2.filename |
|
235 | 235 | assert a2.readable? |
|
236 | 236 | assert (! a2.visible?(User.anonymous)) |
|
237 | 237 | assert a2.visible?(User.find(2)) |
|
238 | 238 | assert a1.created_on < a2.created_on |
|
239 | 239 | |
|
240 | 240 | to_test = { |
|
241 | 241 | 'Inline image: !testfile.png!' => |
|
242 | 242 | 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />', |
|
243 | 243 | 'Inline image: !Testfile.PNG!' => |
|
244 | 244 | 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '/testfile.PNG" alt="" />', |
|
245 | 245 | } |
|
246 | 246 | attachments = [a1, a2] |
|
247 | 247 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) } |
|
248 | 248 | set_tmp_attachments_directory |
|
249 | 249 | end |
|
250 | 250 | |
|
251 | 251 | def test_textile_external_links |
|
252 | 252 | to_test = { |
|
253 | 253 | 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>', |
|
254 | 254 | 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>', |
|
255 | 255 | '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>', |
|
256 | 256 | '"link (Link title with "double-quotes")":http://foo.bar' => '<a href="http://foo.bar" title="Link title with "double-quotes"" class="external">link</a>', |
|
257 | 257 | "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph", |
|
258 | 258 | # no multiline link text |
|
259 | 259 | "This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />and another on a second line\":test", |
|
260 | 260 | # mailto link |
|
261 | 261 | "\"system administrator\":mailto:sysadmin@example.com?subject=redmine%20permissions" => "<a href=\"mailto:sysadmin@example.com?subject=redmine%20permissions\">system administrator</a>", |
|
262 | 262 | # two exclamation marks |
|
263 | 263 | '"a link":http://example.net/path!602815048C7B5C20!302.html' => '<a href="http://example.net/path!602815048C7B5C20!302.html" class="external">a link</a>', |
|
264 | 264 | # escaping |
|
265 | 265 | '"test":http://foo"bar' => '<a href="http://foo"bar" class="external">test</a>', |
|
266 | 266 | } |
|
267 | 267 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
268 | 268 | end |
|
269 | 269 | |
|
270 | 270 | def test_textile_external_links_with_non_ascii_characters |
|
271 | 271 | to_test = { |
|
272 | 272 | %|This is a "link":http://foo.bar/#{@russian_test}| => |
|
273 | 273 | %|This is a <a href="http://foo.bar/#{@russian_test}" class="external">link</a>| |
|
274 | 274 | } |
|
275 | 275 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
276 | 276 | end |
|
277 | 277 | |
|
278 | 278 | def test_redmine_links |
|
279 | 279 | issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3}, |
|
280 | 280 | :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)') |
|
281 | 281 | note_link = link_to('#3-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'}, |
|
282 | 282 | :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)') |
|
283 | 283 | note_link2 = link_to('#3#note-14', {:controller => 'issues', :action => 'show', :id => 3, :anchor => 'note-14'}, |
|
284 | 284 | :class => Issue.find(3).css_classes, :title => 'Bug: Error 281 when updating a recipe (New)') |
|
285 | 285 | |
|
286 | 286 | revision_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1}, |
|
287 | 287 | :class => 'changeset', :title => 'My very first commit do not escaping #<>&') |
|
288 | 288 | revision_link2 = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2}, |
|
289 | 289 | :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3') |
|
290 | 290 | |
|
291 | 291 | changeset_link2 = link_to('691322a8eb01e11fd7', |
|
292 | 292 | {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1}, |
|
293 | 293 | :class => 'changeset', :title => 'My very first commit do not escaping #<>&') |
|
294 | 294 | |
|
295 | 295 | document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1}, |
|
296 | 296 | :class => 'document') |
|
297 | 297 | |
|
298 | 298 | version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2}, |
|
299 | 299 | :class => 'version') |
|
300 | 300 | |
|
301 | 301 | board_url = {:controller => 'boards', :action => 'show', :id => 2, :project_id => 'ecookbook'} |
|
302 | 302 | |
|
303 | 303 | message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4} |
|
304 | 304 | |
|
305 | 305 | news_url = {:controller => 'news', :action => 'show', :id => 1} |
|
306 | 306 | |
|
307 | 307 | project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'} |
|
308 | 308 | |
|
309 | 309 | source_url = '/projects/ecookbook/repository/entry/some/file' |
|
310 | 310 | source_url_with_rev = '/projects/ecookbook/repository/revisions/52/entry/some/file' |
|
311 | 311 | source_url_with_ext = '/projects/ecookbook/repository/entry/some/file.ext' |
|
312 | 312 | source_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/entry/some/file.ext' |
|
313 | 313 | source_url_with_branch = '/projects/ecookbook/repository/revisions/branch/entry/some/file' |
|
314 | 314 | |
|
315 | 315 | export_url = '/projects/ecookbook/repository/raw/some/file' |
|
316 | 316 | export_url_with_rev = '/projects/ecookbook/repository/revisions/52/raw/some/file' |
|
317 | 317 | export_url_with_ext = '/projects/ecookbook/repository/raw/some/file.ext' |
|
318 | 318 | export_url_with_rev_and_ext = '/projects/ecookbook/repository/revisions/52/raw/some/file.ext' |
|
319 | 319 | export_url_with_branch = '/projects/ecookbook/repository/revisions/branch/raw/some/file' |
|
320 | 320 | |
|
321 | 321 | to_test = { |
|
322 | 322 | # tickets |
|
323 | 323 | '#3, [#3], (#3) and #3.' => "#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.", |
|
324 | 324 | # ticket notes |
|
325 | 325 | '#3-14' => note_link, |
|
326 | 326 | '#3#note-14' => note_link2, |
|
327 | 327 | # should not ignore leading zero |
|
328 | 328 | '#03' => '#03', |
|
329 | 329 | # changesets |
|
330 | 330 | 'r1' => revision_link, |
|
331 | 331 | 'r1.' => "#{revision_link}.", |
|
332 | 332 | 'r1, r2' => "#{revision_link}, #{revision_link2}", |
|
333 | 333 | 'r1,r2' => "#{revision_link},#{revision_link2}", |
|
334 | 334 | 'commit:691322a8eb01e11fd7' => changeset_link2, |
|
335 | 335 | # documents |
|
336 | 336 | 'document#1' => document_link, |
|
337 | 337 | 'document:"Test document"' => document_link, |
|
338 | 338 | # versions |
|
339 | 339 | 'version#2' => version_link, |
|
340 | 340 | 'version:1.0' => version_link, |
|
341 | 341 | 'version:"1.0"' => version_link, |
|
342 | 342 | # source |
|
343 | 343 | 'source:some/file' => link_to('source:some/file', source_url, :class => 'source'), |
|
344 | 344 | 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'), |
|
345 | 345 | 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".", |
|
346 | 346 | 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".", |
|
347 | 347 | 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".", |
|
348 | 348 | 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".", |
|
349 | 349 | 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",", |
|
350 | 350 | 'source:/some/file@52' => link_to('source:/some/file@52', source_url_with_rev, :class => 'source'), |
|
351 | 351 | 'source:/some/file@branch' => link_to('source:/some/file@branch', source_url_with_branch, :class => 'source'), |
|
352 | 352 | 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_rev_and_ext, :class => 'source'), |
|
353 | 353 | 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url + "#L110", :class => 'source'), |
|
354 | 354 | 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext + "#L110", :class => 'source'), |
|
355 | 355 | 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url_with_rev + "#L110", :class => 'source'), |
|
356 | 356 | # export |
|
357 | 357 | 'export:/some/file' => link_to('export:/some/file', export_url, :class => 'source download'), |
|
358 | 358 | 'export:/some/file.ext' => link_to('export:/some/file.ext', export_url_with_ext, :class => 'source download'), |
|
359 | 359 | 'export:/some/file@52' => link_to('export:/some/file@52', export_url_with_rev, :class => 'source download'), |
|
360 | 360 | 'export:/some/file.ext@52' => link_to('export:/some/file.ext@52', export_url_with_rev_and_ext, :class => 'source download'), |
|
361 | 361 | 'export:/some/file@branch' => link_to('export:/some/file@branch', export_url_with_branch, :class => 'source download'), |
|
362 | 362 | # forum |
|
363 | 363 | 'forum#2' => link_to('Discussion', board_url, :class => 'board'), |
|
364 | 364 | 'forum:Discussion' => link_to('Discussion', board_url, :class => 'board'), |
|
365 | 365 | # message |
|
366 | 366 | 'message#4' => link_to('Post 2', message_url, :class => 'message'), |
|
367 | 367 | 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'), |
|
368 | 368 | # news |
|
369 | 369 | 'news#1' => link_to('eCookbook first release !', news_url, :class => 'news'), |
|
370 | 370 | 'news:"eCookbook first release !"' => link_to('eCookbook first release !', news_url, :class => 'news'), |
|
371 | 371 | # project |
|
372 | 372 | 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'), |
|
373 | 373 | 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'), |
|
374 | 374 | 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'), |
|
375 | 375 | # not found |
|
376 | 376 | '#0123456789' => '#0123456789', |
|
377 | 377 | # invalid expressions |
|
378 | 378 | 'source:' => 'source:', |
|
379 | 379 | # url hash |
|
380 | 380 | "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>', |
|
381 | 381 | } |
|
382 | 382 | @project = Project.find(1) |
|
383 | 383 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" } |
|
384 | 384 | end |
|
385 | 385 | |
|
386 | 386 | def test_should_not_parse_redmine_links_inside_link |
|
387 | 387 | raw = "r1 should not be parsed in http://example.com/url-r1/" |
|
388 | 388 | assert_match %r{<p><a class="changeset".*>r1</a> should not be parsed in <a class="external" href="http://example.com/url-r1/">http://example.com/url-r1/</a></p>}, |
|
389 | 389 | textilizable(raw, :project => Project.find(1)) |
|
390 | 390 | end |
|
391 | 391 | |
|
392 | 392 | def test_redmine_links_with_a_different_project_before_current_project |
|
393 | 393 | vp1 = Version.generate!(:project_id => 1, :name => '1.4.4') |
|
394 | 394 | vp3 = Version.generate!(:project_id => 3, :name => '1.4.4') |
|
395 | 395 | @project = Project.find(3) |
|
396 | 396 | result1 = link_to("1.4.4", "/versions/#{vp1.id}", :class => "version") |
|
397 | 397 | result2 = link_to("1.4.4", "/versions/#{vp3.id}", :class => "version") |
|
398 | 398 | assert_equal "<p>#{result1} #{result2}</p>", |
|
399 | 399 | textilizable("ecookbook:version:1.4.4 version:1.4.4") |
|
400 | 400 | end |
|
401 | 401 | |
|
402 | 402 | def test_escaped_redmine_links_should_not_be_parsed |
|
403 | 403 | to_test = [ |
|
404 | 404 | '#3.', |
|
405 | 405 | '#3-14.', |
|
406 | 406 | '#3#-note14.', |
|
407 | 407 | 'r1', |
|
408 | 408 | 'document#1', |
|
409 | 409 | 'document:"Test document"', |
|
410 | 410 | 'version#2', |
|
411 | 411 | 'version:1.0', |
|
412 | 412 | 'version:"1.0"', |
|
413 | 413 | 'source:/some/file' |
|
414 | 414 | ] |
|
415 | 415 | @project = Project.find(1) |
|
416 | 416 | to_test.each { |text| assert_equal "<p>#{text}</p>", textilizable("!" + text), "#{text} failed" } |
|
417 | 417 | end |
|
418 | 418 | |
|
419 | 419 | def test_cross_project_redmine_links |
|
420 | 420 | source_link = link_to('ecookbook:source:/some/file', |
|
421 | 421 | {:controller => 'repositories', :action => 'entry', |
|
422 | 422 | :id => 'ecookbook', :path => ['some', 'file']}, |
|
423 | 423 | :class => 'source') |
|
424 | 424 | changeset_link = link_to('ecookbook:r2', |
|
425 | 425 | {:controller => 'repositories', :action => 'revision', |
|
426 | 426 | :id => 'ecookbook', :rev => 2}, |
|
427 | 427 | :class => 'changeset', |
|
428 | 428 | :title => 'This commit fixes #1, #2 and references #1 & #3') |
|
429 | 429 | to_test = { |
|
430 | 430 | # documents |
|
431 | 431 | 'document:"Test document"' => 'document:"Test document"', |
|
432 | 432 | 'ecookbook:document:"Test document"' => |
|
433 | 433 | link_to("Test document", "/documents/1", :class => "document"), |
|
434 | 434 | 'invalid:document:"Test document"' => 'invalid:document:"Test document"', |
|
435 | 435 | # versions |
|
436 | 436 | 'version:"1.0"' => 'version:"1.0"', |
|
437 | 437 | 'ecookbook:version:"1.0"' => |
|
438 | 438 | link_to("1.0", "/versions/2", :class => "version"), |
|
439 | 439 | 'invalid:version:"1.0"' => 'invalid:version:"1.0"', |
|
440 | 440 | # changeset |
|
441 | 441 | 'r2' => 'r2', |
|
442 | 442 | 'ecookbook:r2' => changeset_link, |
|
443 | 443 | 'invalid:r2' => 'invalid:r2', |
|
444 | 444 | # source |
|
445 | 445 | 'source:/some/file' => 'source:/some/file', |
|
446 | 446 | 'ecookbook:source:/some/file' => source_link, |
|
447 | 447 | 'invalid:source:/some/file' => 'invalid:source:/some/file', |
|
448 | 448 | } |
|
449 | 449 | @project = Project.find(3) |
|
450 | 450 | to_test.each do |text, result| |
|
451 | 451 | assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" |
|
452 | 452 | end |
|
453 | 453 | end |
|
454 | 454 | |
|
455 | 455 | def test_redmine_links_by_name_should_work_with_html_escaped_characters |
|
456 | 456 | v = Version.generate!(:name => "Test & Show.txt", :project_id => 1) |
|
457 | 457 | link = link_to("Test & Show.txt", "/versions/#{v.id}", :class => "version") |
|
458 | 458 | |
|
459 | 459 | @project = v.project |
|
460 | 460 | assert_equal "<p>#{link}</p>", textilizable('version:"Test & Show.txt"') |
|
461 | 461 | end |
|
462 | 462 | |
|
463 | 463 | def test_link_to_issue_subject |
|
464 | 464 | issue = Issue.generate!(:subject => "01234567890123456789") |
|
465 | 465 | str = link_to_issue(issue, :truncate => 10) |
|
466 | 466 | result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes) |
|
467 | 467 | assert_equal "#{result}: 0123456...", str |
|
468 | 468 | |
|
469 | 469 | issue = Issue.generate!(:subject => "<&>") |
|
470 | 470 | str = link_to_issue(issue) |
|
471 | 471 | result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes) |
|
472 | 472 | assert_equal "#{result}: <&>", str |
|
473 | 473 | |
|
474 | 474 | issue = Issue.generate!(:subject => "<&>0123456789012345") |
|
475 | 475 | str = link_to_issue(issue, :truncate => 10) |
|
476 | 476 | result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", :class => issue.css_classes) |
|
477 | 477 | assert_equal "#{result}: <&>0123...", str |
|
478 | 478 | end |
|
479 | 479 | |
|
480 | 480 | def test_link_to_issue_title |
|
481 | 481 | long_str = "0123456789" * 5 |
|
482 | 482 | |
|
483 | 483 | issue = Issue.generate!(:subject => "#{long_str}01234567890123456789") |
|
484 | 484 | str = link_to_issue(issue, :subject => false) |
|
485 | 485 | result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", |
|
486 | 486 | :class => issue.css_classes, |
|
487 | 487 | :title => "#{long_str}0123456...") |
|
488 | 488 | assert_equal result, str |
|
489 | 489 | |
|
490 | 490 | issue = Issue.generate!(:subject => "<&>#{long_str}01234567890123456789") |
|
491 | 491 | str = link_to_issue(issue, :subject => false) |
|
492 | 492 | result = link_to("Bug ##{issue.id}", "/issues/#{issue.id}", |
|
493 | 493 | :class => issue.css_classes, |
|
494 | 494 | :title => "<&>#{long_str}0123...") |
|
495 | 495 | assert_equal result, str |
|
496 | 496 | end |
|
497 | 497 | |
|
498 | 498 | def test_multiple_repositories_redmine_links |
|
499 | 499 | svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn_repo-1', :url => 'file:///foo/hg') |
|
500 | 500 | Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123') |
|
501 | 501 | hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg') |
|
502 | 502 | Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd') |
|
503 | 503 | |
|
504 | 504 | changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2}, |
|
505 | 505 | :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3') |
|
506 | 506 | svn_changeset_link = link_to('svn_repo-1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn_repo-1', :rev => 123}, |
|
507 | 507 | :class => 'changeset', :title => '') |
|
508 | 508 | hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'}, |
|
509 | 509 | :class => 'changeset', :title => '') |
|
510 | 510 | |
|
511 | 511 | source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source') |
|
512 | 512 | hg_source_link = link_to('source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source') |
|
513 | 513 | |
|
514 | 514 | to_test = { |
|
515 | 515 | 'r2' => changeset_link, |
|
516 | 516 | 'svn_repo-1|r123' => svn_changeset_link, |
|
517 | 517 | 'invalid|r123' => 'invalid|r123', |
|
518 | 518 | 'commit:hg1|abcd' => hg_changeset_link, |
|
519 | 519 | 'commit:invalid|abcd' => 'commit:invalid|abcd', |
|
520 | 520 | # source |
|
521 | 521 | 'source:some/file' => source_link, |
|
522 | 522 | 'source:hg1|some/file' => hg_source_link, |
|
523 | 523 | 'source:invalid|some/file' => 'source:invalid|some/file', |
|
524 | 524 | } |
|
525 | 525 | |
|
526 | 526 | @project = Project.find(1) |
|
527 | 527 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" } |
|
528 | 528 | end |
|
529 | 529 | |
|
530 | 530 | def test_cross_project_multiple_repositories_redmine_links |
|
531 | 531 | svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg') |
|
532 | 532 | Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123') |
|
533 | 533 | hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg') |
|
534 | 534 | Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd') |
|
535 | 535 | |
|
536 | 536 | changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2}, |
|
537 | 537 | :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3') |
|
538 | 538 | svn_changeset_link = link_to('ecookbook:svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123}, |
|
539 | 539 | :class => 'changeset', :title => '') |
|
540 | 540 | hg_changeset_link = link_to('ecookbook:hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'}, |
|
541 | 541 | :class => 'changeset', :title => '') |
|
542 | 542 | |
|
543 | 543 | source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source') |
|
544 | 544 | hg_source_link = link_to('ecookbook:source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source') |
|
545 | 545 | |
|
546 | 546 | to_test = { |
|
547 | 547 | 'ecookbook:r2' => changeset_link, |
|
548 | 548 | 'ecookbook:svn1|r123' => svn_changeset_link, |
|
549 | 549 | 'ecookbook:invalid|r123' => 'ecookbook:invalid|r123', |
|
550 | 550 | 'ecookbook:commit:hg1|abcd' => hg_changeset_link, |
|
551 | 551 | 'ecookbook:commit:invalid|abcd' => 'ecookbook:commit:invalid|abcd', |
|
552 | 552 | 'invalid:commit:invalid|abcd' => 'invalid:commit:invalid|abcd', |
|
553 | 553 | # source |
|
554 | 554 | 'ecookbook:source:some/file' => source_link, |
|
555 | 555 | 'ecookbook:source:hg1|some/file' => hg_source_link, |
|
556 | 556 | 'ecookbook:source:invalid|some/file' => 'ecookbook:source:invalid|some/file', |
|
557 | 557 | 'invalid:source:invalid|some/file' => 'invalid:source:invalid|some/file', |
|
558 | 558 | } |
|
559 | 559 | |
|
560 | 560 | @project = Project.find(3) |
|
561 | 561 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" } |
|
562 | 562 | end |
|
563 | 563 | |
|
564 | 564 | def test_redmine_links_git_commit |
|
565 | 565 | changeset_link = link_to('abcd', |
|
566 | 566 | { |
|
567 | 567 | :controller => 'repositories', |
|
568 | 568 | :action => 'revision', |
|
569 | 569 | :id => 'subproject1', |
|
570 | 570 | :rev => 'abcd', |
|
571 | 571 | }, |
|
572 | 572 | :class => 'changeset', :title => 'test commit') |
|
573 | 573 | to_test = { |
|
574 | 574 | 'commit:abcd' => changeset_link, |
|
575 | 575 | } |
|
576 | 576 | @project = Project.find(3) |
|
577 | 577 | r = Repository::Git.create!(:project => @project, :url => '/tmp/test/git') |
|
578 | 578 | assert r |
|
579 | 579 | c = Changeset.new(:repository => r, |
|
580 | 580 | :committed_on => Time.now, |
|
581 | 581 | :revision => 'abcd', |
|
582 | 582 | :scmid => 'abcd', |
|
583 | 583 | :comments => 'test commit') |
|
584 | 584 | assert( c.save ) |
|
585 | 585 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
586 | 586 | end |
|
587 | 587 | |
|
588 | 588 | # TODO: Bazaar commit id contains mail address, so it contains '@' and '_'. |
|
589 | 589 | def test_redmine_links_darcs_commit |
|
590 | 590 | changeset_link = link_to('20080308225258-98289-abcd456efg.gz', |
|
591 | 591 | { |
|
592 | 592 | :controller => 'repositories', |
|
593 | 593 | :action => 'revision', |
|
594 | 594 | :id => 'subproject1', |
|
595 | 595 | :rev => '123', |
|
596 | 596 | }, |
|
597 | 597 | :class => 'changeset', :title => 'test commit') |
|
598 | 598 | to_test = { |
|
599 | 599 | 'commit:20080308225258-98289-abcd456efg.gz' => changeset_link, |
|
600 | 600 | } |
|
601 | 601 | @project = Project.find(3) |
|
602 | 602 | r = Repository::Darcs.create!( |
|
603 | 603 | :project => @project, :url => '/tmp/test/darcs', |
|
604 | 604 | :log_encoding => 'UTF-8') |
|
605 | 605 | assert r |
|
606 | 606 | c = Changeset.new(:repository => r, |
|
607 | 607 | :committed_on => Time.now, |
|
608 | 608 | :revision => '123', |
|
609 | 609 | :scmid => '20080308225258-98289-abcd456efg.gz', |
|
610 | 610 | :comments => 'test commit') |
|
611 | 611 | assert( c.save ) |
|
612 | 612 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
613 | 613 | end |
|
614 | 614 | |
|
615 | 615 | def test_redmine_links_mercurial_commit |
|
616 | 616 | changeset_link_rev = link_to('r123', |
|
617 | 617 | { |
|
618 | 618 | :controller => 'repositories', |
|
619 | 619 | :action => 'revision', |
|
620 | 620 | :id => 'subproject1', |
|
621 | 621 | :rev => '123' , |
|
622 | 622 | }, |
|
623 | 623 | :class => 'changeset', :title => 'test commit') |
|
624 | 624 | changeset_link_commit = link_to('abcd', |
|
625 | 625 | { |
|
626 | 626 | :controller => 'repositories', |
|
627 | 627 | :action => 'revision', |
|
628 | 628 | :id => 'subproject1', |
|
629 | 629 | :rev => 'abcd' , |
|
630 | 630 | }, |
|
631 | 631 | :class => 'changeset', :title => 'test commit') |
|
632 | 632 | to_test = { |
|
633 | 633 | 'r123' => changeset_link_rev, |
|
634 | 634 | 'commit:abcd' => changeset_link_commit, |
|
635 | 635 | } |
|
636 | 636 | @project = Project.find(3) |
|
637 | 637 | r = Repository::Mercurial.create!(:project => @project, :url => '/tmp/test') |
|
638 | 638 | assert r |
|
639 | 639 | c = Changeset.new(:repository => r, |
|
640 | 640 | :committed_on => Time.now, |
|
641 | 641 | :revision => '123', |
|
642 | 642 | :scmid => 'abcd', |
|
643 | 643 | :comments => 'test commit') |
|
644 | 644 | assert( c.save ) |
|
645 | 645 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
646 | 646 | end |
|
647 | 647 | |
|
648 | 648 | def test_attachment_links |
|
649 | 649 | text = 'attachment:error281.txt' |
|
650 | 650 | result = link_to("error281.txt", "/attachments/download/1/error281.txt", |
|
651 | 651 | :class => "attachment") |
|
652 | 652 | assert_equal "<p>#{result}</p>", |
|
653 | 653 | textilizable(text, |
|
654 | 654 | :attachments => Issue.find(3).attachments), |
|
655 | 655 | "#{text} failed" |
|
656 | 656 | end |
|
657 | 657 | |
|
658 | 658 | def test_attachment_link_should_link_to_latest_attachment |
|
659 | 659 | set_tmp_attachments_directory |
|
660 | 660 | a1 = Attachment.generate!(:filename => "test.txt", :created_on => 1.hour.ago) |
|
661 | 661 | a2 = Attachment.generate!(:filename => "test.txt") |
|
662 | 662 | result = link_to("test.txt", "/attachments/download/#{a2.id}/test.txt", |
|
663 | 663 | :class => "attachment") |
|
664 | 664 | assert_equal "<p>#{result}</p>", |
|
665 | 665 | textilizable('attachment:test.txt', :attachments => [a1, a2]) |
|
666 | 666 | end |
|
667 | 667 | |
|
668 | 668 | def test_wiki_links |
|
669 | 669 | russian_eacape = CGI.escape(@russian_test) |
|
670 | 670 | to_test = { |
|
671 | 671 | '[[CookBook documentation]]' => |
|
672 | 672 | link_to("CookBook documentation", |
|
673 | 673 | "/projects/ecookbook/wiki/CookBook_documentation", |
|
674 | 674 | :class => "wiki-page"), |
|
675 | 675 | '[[Another page|Page]]' => |
|
676 | 676 | link_to("Page", |
|
677 | 677 | "/projects/ecookbook/wiki/Another_page", |
|
678 | 678 | :class => "wiki-page"), |
|
679 | 679 | # title content should be formatted |
|
680 | 680 | '[[Another page|With _styled_ *title*]]' => |
|
681 | 681 | link_to("With <em>styled</em> <strong>title</strong>".html_safe, |
|
682 | 682 | "/projects/ecookbook/wiki/Another_page", |
|
683 | 683 | :class => "wiki-page"), |
|
684 | 684 | '[[Another page|With title containing <strong>HTML entities & markups</strong>]]' => |
|
685 | 685 | link_to("With title containing <strong>HTML entities & markups</strong>".html_safe, |
|
686 | 686 | "/projects/ecookbook/wiki/Another_page", |
|
687 | 687 | :class => "wiki-page"), |
|
688 | 688 | # link with anchor |
|
689 | 689 | '[[CookBook documentation#One-section]]' => |
|
690 | 690 | link_to("CookBook documentation", |
|
691 | 691 | "/projects/ecookbook/wiki/CookBook_documentation#One-section", |
|
692 | 692 | :class => "wiki-page"), |
|
693 | 693 | '[[Another page#anchor|Page]]' => |
|
694 | 694 | link_to("Page", |
|
695 | 695 | "/projects/ecookbook/wiki/Another_page#anchor", |
|
696 | 696 | :class => "wiki-page"), |
|
697 | 697 | # UTF8 anchor |
|
698 | 698 | "[[Another_page##{@russian_test}|#{@russian_test}]]" => |
|
699 | 699 | link_to(@russian_test, |
|
700 | 700 | "/projects/ecookbook/wiki/Another_page##{russian_eacape}", |
|
701 | 701 | :class => "wiki-page"), |
|
702 | 702 | # page that doesn't exist |
|
703 | 703 | '[[Unknown page]]' => |
|
704 | 704 | link_to("Unknown page", |
|
705 | 705 | "/projects/ecookbook/wiki/Unknown_page", |
|
706 | 706 | :class => "wiki-page new"), |
|
707 | 707 | '[[Unknown page|404]]' => |
|
708 | 708 | link_to("404", |
|
709 | 709 | "/projects/ecookbook/wiki/Unknown_page", |
|
710 | 710 | :class => "wiki-page new"), |
|
711 | 711 | # link to another project wiki |
|
712 | 712 | '[[onlinestore:]]' => |
|
713 | 713 | link_to("onlinestore", |
|
714 | 714 | "/projects/onlinestore/wiki", |
|
715 | 715 | :class => "wiki-page"), |
|
716 | 716 | '[[onlinestore:|Wiki]]' => |
|
717 | 717 | link_to("Wiki", |
|
718 | 718 | "/projects/onlinestore/wiki", |
|
719 | 719 | :class => "wiki-page"), |
|
720 | 720 | '[[onlinestore:Start page]]' => |
|
721 | 721 | link_to("Start page", |
|
722 | 722 | "/projects/onlinestore/wiki/Start_page", |
|
723 | 723 | :class => "wiki-page"), |
|
724 | 724 | '[[onlinestore:Start page|Text]]' => |
|
725 | 725 | link_to("Text", |
|
726 | 726 | "/projects/onlinestore/wiki/Start_page", |
|
727 | 727 | :class => "wiki-page"), |
|
728 | 728 | '[[onlinestore:Unknown page]]' => |
|
729 | 729 | link_to("Unknown page", |
|
730 | 730 | "/projects/onlinestore/wiki/Unknown_page", |
|
731 | 731 | :class => "wiki-page new"), |
|
732 | 732 | # struck through link |
|
733 | 733 | '-[[Another page|Page]]-' => |
|
734 | 734 | "<del>".html_safe + |
|
735 | 735 | link_to("Page", |
|
736 | 736 | "/projects/ecookbook/wiki/Another_page", |
|
737 | 737 | :class => "wiki-page").html_safe + |
|
738 | 738 | "</del>".html_safe, |
|
739 | 739 | '-[[Another page|Page]] link-' => |
|
740 | 740 | "<del>".html_safe + |
|
741 | 741 | link_to("Page", |
|
742 | 742 | "/projects/ecookbook/wiki/Another_page", |
|
743 | 743 | :class => "wiki-page").html_safe + |
|
744 | 744 | " link</del>".html_safe, |
|
745 | 745 | # escaping |
|
746 | 746 | '![[Another page|Page]]' => '[[Another page|Page]]', |
|
747 | 747 | # project does not exist |
|
748 | 748 | '[[unknowproject:Start]]' => '[[unknowproject:Start]]', |
|
749 | 749 | '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]', |
|
750 | 750 | } |
|
751 | 751 | @project = Project.find(1) |
|
752 | 752 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
753 | 753 | end |
|
754 | 754 | |
|
755 | 755 | def test_wiki_links_within_local_file_generation_context |
|
756 | 756 | to_test = { |
|
757 | 757 | # link to a page |
|
758 | 758 | '[[CookBook documentation]]' => |
|
759 | 759 | link_to("CookBook documentation", "CookBook_documentation.html", |
|
760 | 760 | :class => "wiki-page"), |
|
761 | 761 | '[[CookBook documentation|documentation]]' => |
|
762 | 762 | link_to("documentation", "CookBook_documentation.html", |
|
763 | 763 | :class => "wiki-page"), |
|
764 | 764 | '[[CookBook documentation#One-section]]' => |
|
765 | 765 | link_to("CookBook documentation", "CookBook_documentation.html#One-section", |
|
766 | 766 | :class => "wiki-page"), |
|
767 | 767 | '[[CookBook documentation#One-section|documentation]]' => |
|
768 | 768 | link_to("documentation", "CookBook_documentation.html#One-section", |
|
769 | 769 | :class => "wiki-page"), |
|
770 | 770 | # page that doesn't exist |
|
771 | 771 | '[[Unknown page]]' => |
|
772 | 772 | link_to("Unknown page", "Unknown_page.html", |
|
773 | 773 | :class => "wiki-page new"), |
|
774 | 774 | '[[Unknown page|404]]' => |
|
775 | 775 | link_to("404", "Unknown_page.html", |
|
776 | 776 | :class => "wiki-page new"), |
|
777 | 777 | '[[Unknown page#anchor]]' => |
|
778 | 778 | link_to("Unknown page", "Unknown_page.html#anchor", |
|
779 | 779 | :class => "wiki-page new"), |
|
780 | 780 | '[[Unknown page#anchor|404]]' => |
|
781 | 781 | link_to("404", "Unknown_page.html#anchor", |
|
782 | 782 | :class => "wiki-page new"), |
|
783 | 783 | } |
|
784 | 784 | @project = Project.find(1) |
|
785 | 785 | to_test.each do |text, result| |
|
786 | 786 | assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :local) |
|
787 | 787 | end |
|
788 | 788 | end |
|
789 | 789 | |
|
790 | 790 | def test_wiki_links_within_wiki_page_context |
|
791 | 791 | page = WikiPage.find_by_title('Another_page' ) |
|
792 | 792 | to_test = { |
|
793 | 793 | '[[CookBook documentation]]' => |
|
794 | 794 | link_to("CookBook documentation", |
|
795 | 795 | "/projects/ecookbook/wiki/CookBook_documentation", |
|
796 | 796 | :class => "wiki-page"), |
|
797 | 797 | '[[CookBook documentation|documentation]]' => |
|
798 | 798 | link_to("documentation", |
|
799 | 799 | "/projects/ecookbook/wiki/CookBook_documentation", |
|
800 | 800 | :class => "wiki-page"), |
|
801 | 801 | '[[CookBook documentation#One-section]]' => |
|
802 | 802 | link_to("CookBook documentation", |
|
803 | 803 | "/projects/ecookbook/wiki/CookBook_documentation#One-section", |
|
804 | 804 | :class => "wiki-page"), |
|
805 | 805 | '[[CookBook documentation#One-section|documentation]]' => |
|
806 | 806 | link_to("documentation", |
|
807 | 807 | "/projects/ecookbook/wiki/CookBook_documentation#One-section", |
|
808 | 808 | :class => "wiki-page"), |
|
809 | 809 | # link to the current page |
|
810 | 810 | '[[Another page]]' => |
|
811 | 811 | link_to("Another page", |
|
812 | 812 | "/projects/ecookbook/wiki/Another_page", |
|
813 | 813 | :class => "wiki-page"), |
|
814 | 814 | '[[Another page|Page]]' => |
|
815 | 815 | link_to("Page", |
|
816 | 816 | "/projects/ecookbook/wiki/Another_page", |
|
817 | 817 | :class => "wiki-page"), |
|
818 | 818 | '[[Another page#anchor]]' => |
|
819 | 819 | link_to("Another page", |
|
820 | 820 | "#anchor", |
|
821 | 821 | :class => "wiki-page"), |
|
822 | 822 | '[[Another page#anchor|Page]]' => |
|
823 | 823 | link_to("Page", |
|
824 | 824 | "#anchor", |
|
825 | 825 | :class => "wiki-page"), |
|
826 | 826 | # page that doesn't exist |
|
827 | 827 | '[[Unknown page]]' => |
|
828 | 828 | link_to("Unknown page", |
|
829 | 829 | "/projects/ecookbook/wiki/Unknown_page?parent=Another_page", |
|
830 | 830 | :class => "wiki-page new"), |
|
831 | 831 | '[[Unknown page|404]]' => |
|
832 | 832 | link_to("404", |
|
833 | 833 | "/projects/ecookbook/wiki/Unknown_page?parent=Another_page", |
|
834 | 834 | :class => "wiki-page new"), |
|
835 | 835 | '[[Unknown page#anchor]]' => |
|
836 | 836 | link_to("Unknown page", |
|
837 | 837 | "/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor", |
|
838 | 838 | :class => "wiki-page new"), |
|
839 | 839 | '[[Unknown page#anchor|404]]' => |
|
840 | 840 | link_to("404", |
|
841 | 841 | "/projects/ecookbook/wiki/Unknown_page?parent=Another_page#anchor", |
|
842 | 842 | :class => "wiki-page new"), |
|
843 | 843 | } |
|
844 | 844 | @project = Project.find(1) |
|
845 | 845 | to_test.each do |text, result| |
|
846 | 846 | assert_equal "<p>#{result}</p>", |
|
847 | 847 | textilizable(WikiContent.new( :text => text, :page => page ), :text) |
|
848 | 848 | end |
|
849 | 849 | end |
|
850 | 850 | |
|
851 | 851 | def test_wiki_links_anchor_option_should_prepend_page_title_to_href |
|
852 | 852 | to_test = { |
|
853 | 853 | # link to a page |
|
854 | 854 | '[[CookBook documentation]]' => |
|
855 | 855 | link_to("CookBook documentation", |
|
856 | 856 | "#CookBook_documentation", |
|
857 | 857 | :class => "wiki-page"), |
|
858 | 858 | '[[CookBook documentation|documentation]]' => |
|
859 | 859 | link_to("documentation", |
|
860 | 860 | "#CookBook_documentation", |
|
861 | 861 | :class => "wiki-page"), |
|
862 | 862 | '[[CookBook documentation#One-section]]' => |
|
863 | 863 | link_to("CookBook documentation", |
|
864 | 864 | "#CookBook_documentation_One-section", |
|
865 | 865 | :class => "wiki-page"), |
|
866 | 866 | '[[CookBook documentation#One-section|documentation]]' => |
|
867 | 867 | link_to("documentation", |
|
868 | 868 | "#CookBook_documentation_One-section", |
|
869 | 869 | :class => "wiki-page"), |
|
870 | 870 | # page that doesn't exist |
|
871 | 871 | '[[Unknown page]]' => |
|
872 | 872 | link_to("Unknown page", |
|
873 | 873 | "#Unknown_page", |
|
874 | 874 | :class => "wiki-page new"), |
|
875 | 875 | '[[Unknown page|404]]' => |
|
876 | 876 | link_to("404", |
|
877 | 877 | "#Unknown_page", |
|
878 | 878 | :class => "wiki-page new"), |
|
879 | 879 | '[[Unknown page#anchor]]' => |
|
880 | 880 | link_to("Unknown page", |
|
881 | 881 | "#Unknown_page_anchor", |
|
882 | 882 | :class => "wiki-page new"), |
|
883 | 883 | '[[Unknown page#anchor|404]]' => |
|
884 | 884 | link_to("404", |
|
885 | 885 | "#Unknown_page_anchor", |
|
886 | 886 | :class => "wiki-page new"), |
|
887 | 887 | } |
|
888 | 888 | @project = Project.find(1) |
|
889 | 889 | to_test.each do |text, result| |
|
890 | 890 | assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :anchor) |
|
891 | 891 | end |
|
892 | 892 | end |
|
893 | 893 | |
|
894 | 894 | def test_html_tags |
|
895 | 895 | to_test = { |
|
896 | 896 | "<div>content</div>" => "<p><div>content</div></p>", |
|
897 | 897 | "<div class=\"bold\">content</div>" => "<p><div class=\"bold\">content</div></p>", |
|
898 | 898 | "<script>some script;</script>" => "<p><script>some script;</script></p>", |
|
899 | 899 | # do not escape pre/code tags |
|
900 | 900 | "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>", |
|
901 | 901 | "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>", |
|
902 | 902 | "<pre><div>content</div></pre>" => "<pre><div>content</div></pre>", |
|
903 | 903 | "HTML comment: <!-- no comments -->" => "<p>HTML comment: <!-- no comments --></p>", |
|
904 | 904 | "<!-- opening comment" => "<p><!-- opening comment</p>", |
|
905 | 905 | # remove attributes except class |
|
906 | 906 | "<pre class='foo'>some text</pre>" => "<pre class='foo'>some text</pre>", |
|
907 | 907 | '<pre class="foo">some text</pre>' => '<pre class="foo">some text</pre>', |
|
908 | 908 | "<pre class='foo bar'>some text</pre>" => "<pre class='foo bar'>some text</pre>", |
|
909 | 909 | '<pre class="foo bar">some text</pre>' => '<pre class="foo bar">some text</pre>', |
|
910 | 910 | "<pre onmouseover='alert(1)'>some text</pre>" => "<pre>some text</pre>", |
|
911 | 911 | # xss |
|
912 | 912 | '<pre><code class=""onmouseover="alert(1)">text</code></pre>' => '<pre><code>text</code></pre>', |
|
913 | 913 | '<pre class=""onmouseover="alert(1)">text</pre>' => '<pre>text</pre>', |
|
914 | 914 | } |
|
915 | 915 | to_test.each { |text, result| assert_equal result, textilizable(text) } |
|
916 | 916 | end |
|
917 | 917 | |
|
918 | 918 | def test_allowed_html_tags |
|
919 | 919 | to_test = { |
|
920 | 920 | "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>", |
|
921 | 921 | "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting", |
|
922 | 922 | "<notextile>this is <tag>a tag</tag></notextile>" => "this is <tag>a tag</tag>" |
|
923 | 923 | } |
|
924 | 924 | to_test.each { |text, result| assert_equal result, textilizable(text) } |
|
925 | 925 | end |
|
926 | 926 | |
|
927 | 927 | def test_pre_tags |
|
928 | 928 | raw = <<-RAW |
|
929 | 929 | Before |
|
930 | 930 | |
|
931 | 931 | <pre> |
|
932 | 932 | <prepared-statement-cache-size>32</prepared-statement-cache-size> |
|
933 | 933 | </pre> |
|
934 | 934 | |
|
935 | 935 | After |
|
936 | 936 | RAW |
|
937 | 937 | |
|
938 | 938 | expected = <<-EXPECTED |
|
939 | 939 | <p>Before</p> |
|
940 | 940 | <pre> |
|
941 | 941 | <prepared-statement-cache-size>32</prepared-statement-cache-size> |
|
942 | 942 | </pre> |
|
943 | 943 | <p>After</p> |
|
944 | 944 | EXPECTED |
|
945 | 945 | |
|
946 | 946 | assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '') |
|
947 | 947 | end |
|
948 | 948 | |
|
949 | 949 | def test_pre_content_should_not_parse_wiki_and_redmine_links |
|
950 | 950 | raw = <<-RAW |
|
951 | 951 | [[CookBook documentation]] |
|
952 | 952 | |
|
953 | 953 | #1 |
|
954 | 954 | |
|
955 | 955 | <pre> |
|
956 | 956 | [[CookBook documentation]] |
|
957 | 957 | |
|
958 | 958 | #1 |
|
959 | 959 | </pre> |
|
960 | 960 | RAW |
|
961 | 961 | |
|
962 | 962 | result1 = link_to("CookBook documentation", |
|
963 | 963 | "/projects/ecookbook/wiki/CookBook_documentation", |
|
964 | 964 | :class => "wiki-page") |
|
965 | 965 | result2 = link_to('#1', |
|
966 | 966 | "/issues/1", |
|
967 | 967 | :class => Issue.find(1).css_classes, |
|
968 | 968 | :title => "Bug: Cannot print recipes (New)") |
|
969 | 969 | |
|
970 | 970 | expected = <<-EXPECTED |
|
971 | 971 | <p>#{result1}</p> |
|
972 | 972 | <p>#{result2}</p> |
|
973 | 973 | <pre> |
|
974 | 974 | [[CookBook documentation]] |
|
975 | 975 | |
|
976 | 976 | #1 |
|
977 | 977 | </pre> |
|
978 | 978 | EXPECTED |
|
979 | 979 | |
|
980 | 980 | @project = Project.find(1) |
|
981 | 981 | assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '') |
|
982 | 982 | end |
|
983 | 983 | |
|
984 | 984 | def test_non_closing_pre_blocks_should_be_closed |
|
985 | 985 | raw = <<-RAW |
|
986 | 986 | <pre><code> |
|
987 | 987 | RAW |
|
988 | 988 | |
|
989 | 989 | expected = <<-EXPECTED |
|
990 | 990 | <pre><code> |
|
991 | 991 | </code></pre> |
|
992 | 992 | EXPECTED |
|
993 | 993 | |
|
994 | 994 | @project = Project.find(1) |
|
995 | 995 | assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '') |
|
996 | 996 | end |
|
997 | 997 | |
|
998 | 998 | def test_unbalanced_closing_pre_tag_should_not_error |
|
999 | 999 | assert_nothing_raised do |
|
1000 | 1000 | textilizable("unbalanced</pre>") |
|
1001 | 1001 | end |
|
1002 | 1002 | end |
|
1003 | 1003 | |
|
1004 | 1004 | def test_syntax_highlight |
|
1005 | 1005 | raw = <<-RAW |
|
1006 | 1006 | <pre><code class="ruby"> |
|
1007 | 1007 | # Some ruby code here |
|
1008 | 1008 | </code></pre> |
|
1009 | 1009 | RAW |
|
1010 | 1010 | |
|
1011 | 1011 | expected = <<-EXPECTED |
|
1012 | 1012 | <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="comment"># Some ruby code here</span></span> |
|
1013 | 1013 | </code></pre> |
|
1014 | 1014 | EXPECTED |
|
1015 | 1015 | |
|
1016 | 1016 | assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '') |
|
1017 | 1017 | end |
|
1018 | 1018 | |
|
1019 | 1019 | def test_to_path_param |
|
1020 | 1020 | assert_equal 'test1/test2', to_path_param('test1/test2') |
|
1021 | 1021 | assert_equal 'test1/test2', to_path_param('/test1/test2/') |
|
1022 | 1022 | assert_equal 'test1/test2', to_path_param('//test1/test2/') |
|
1023 | 1023 | assert_equal nil, to_path_param('/') |
|
1024 | 1024 | end |
|
1025 | 1025 | |
|
1026 | 1026 | def test_wiki_links_in_tables |
|
1027 | 1027 | text = "|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" |
|
1028 | 1028 | link1 = link_to("Link title", "/projects/ecookbook/wiki/Page", :class => "wiki-page new") |
|
1029 | 1029 | link2 = link_to("Other title", "/projects/ecookbook/wiki/Other_Page", :class => "wiki-page new") |
|
1030 | 1030 | link3 = link_to("Last page", "/projects/ecookbook/wiki/Last_page", :class => "wiki-page new") |
|
1031 | 1031 | result = "<tr><td>#{link1}</td>" + |
|
1032 | 1032 | "<td>#{link2}</td>" + |
|
1033 | 1033 | "</tr><tr><td>Cell 21</td><td>#{link3}</td></tr>" |
|
1034 | 1034 | @project = Project.find(1) |
|
1035 | 1035 | assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') |
|
1036 | 1036 | end |
|
1037 | 1037 | |
|
1038 | 1038 | def test_text_formatting |
|
1039 | 1039 | to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>', |
|
1040 | 1040 | '(_text within parentheses_)' => '(<em>text within parentheses</em>)', |
|
1041 | 1041 | 'a *Humane Web* Text Generator' => 'a <strong>Humane Web</strong> Text Generator', |
|
1042 | 1042 | 'a H *umane* W *eb* T *ext* G *enerator*' => 'a H <strong>umane</strong> W <strong>eb</strong> T <strong>ext</strong> G <strong>enerator</strong>', |
|
1043 | 1043 | 'a *H* umane *W* eb *T* ext *G* enerator' => 'a <strong>H</strong> umane <strong>W</strong> eb <strong>T</strong> ext <strong>G</strong> enerator', |
|
1044 | 1044 | } |
|
1045 | 1045 | to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } |
|
1046 | 1046 | end |
|
1047 | 1047 | |
|
1048 | 1048 | def test_wiki_horizontal_rule |
|
1049 | 1049 | assert_equal '<hr />', textilizable('---') |
|
1050 | 1050 | assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---') |
|
1051 | 1051 | end |
|
1052 | 1052 | |
|
1053 | 1053 | def test_footnotes |
|
1054 | 1054 | raw = <<-RAW |
|
1055 | 1055 | This is some text[1]. |
|
1056 | 1056 | |
|
1057 | 1057 | fn1. This is the foot note |
|
1058 | 1058 | RAW |
|
1059 | 1059 | |
|
1060 | 1060 | expected = <<-EXPECTED |
|
1061 | 1061 | <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p> |
|
1062 | 1062 | <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p> |
|
1063 | 1063 | EXPECTED |
|
1064 | 1064 | |
|
1065 | 1065 | assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '') |
|
1066 | 1066 | end |
|
1067 | 1067 | |
|
1068 | 1068 | def test_headings |
|
1069 | 1069 | raw = 'h1. Some heading' |
|
1070 | 1070 | expected = %|<a name="Some-heading"></a>\n<h1 >Some heading<a href="#Some-heading" class="wiki-anchor">¶</a></h1>| |
|
1071 | 1071 | |
|
1072 | 1072 | assert_equal expected, textilizable(raw) |
|
1073 | 1073 | end |
|
1074 | 1074 | |
|
1075 | 1075 | def test_headings_with_special_chars |
|
1076 | 1076 | # This test makes sure that the generated anchor names match the expected |
|
1077 | 1077 | # ones even if the heading text contains unconventional characters |
|
1078 | 1078 | raw = 'h1. Some heading related to version 0.5' |
|
1079 | 1079 | anchor = sanitize_anchor_name("Some-heading-related-to-version-0.5") |
|
1080 | 1080 | expected = %|<a name="#{anchor}"></a>\n<h1 >Some heading related to version 0.5<a href="##{anchor}" class="wiki-anchor">¶</a></h1>| |
|
1081 | 1081 | |
|
1082 | 1082 | assert_equal expected, textilizable(raw) |
|
1083 | 1083 | end |
|
1084 | 1084 | |
|
1085 | 1085 | def test_headings_in_wiki_single_page_export_should_be_prepended_with_page_title |
|
1086 | 1086 | page = WikiPage.new( :title => 'Page Title', :wiki_id => 1 ) |
|
1087 | 1087 | content = WikiContent.new( :text => 'h1. Some heading', :page => page ) |
|
1088 | 1088 | |
|
1089 | 1089 | expected = %|<a name="Page_Title_Some-heading"></a>\n<h1 >Some heading<a href="#Page_Title_Some-heading" class="wiki-anchor">¶</a></h1>| |
|
1090 | 1090 | |
|
1091 | 1091 | assert_equal expected, textilizable(content, :text, :wiki_links => :anchor ) |
|
1092 | 1092 | end |
|
1093 | 1093 | |
|
1094 | 1094 | def test_table_of_content |
|
1095 | 1095 | raw = <<-RAW |
|
1096 | 1096 | {{toc}} |
|
1097 | 1097 | |
|
1098 | 1098 | h1. Title |
|
1099 | 1099 | |
|
1100 | 1100 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero. |
|
1101 | 1101 | |
|
1102 | 1102 | h2. Subtitle with a [[Wiki]] link |
|
1103 | 1103 | |
|
1104 | 1104 | Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor. |
|
1105 | 1105 | |
|
1106 | 1106 | h2. Subtitle with [[Wiki|another Wiki]] link |
|
1107 | 1107 | |
|
1108 | 1108 | h2. Subtitle with %{color:red}red text% |
|
1109 | 1109 | |
|
1110 | 1110 | <pre> |
|
1111 | 1111 | some code |
|
1112 | 1112 | </pre> |
|
1113 | 1113 | |
|
1114 | 1114 | h3. Subtitle with *some* _modifiers_ |
|
1115 | 1115 | |
|
1116 | 1116 | h3. Subtitle with @inline code@ |
|
1117 | 1117 | |
|
1118 | 1118 | h1. Another title |
|
1119 | 1119 | |
|
1120 | 1120 | h3. An "Internet link":http://www.redmine.org/ inside subtitle |
|
1121 | 1121 | |
|
1122 | 1122 | h2. "Project Name !/attachments/1234/logo_small.gif! !/attachments/5678/logo_2.png!":/projects/projectname/issues |
|
1123 | 1123 | |
|
1124 | 1124 | RAW |
|
1125 | 1125 | |
|
1126 | 1126 | expected = '<ul class="toc">' + |
|
1127 | 1127 | '<li><a href="#Title">Title</a>' + |
|
1128 | 1128 | '<ul>' + |
|
1129 | 1129 | '<li><a href="#Subtitle-with-a-Wiki-link">Subtitle with a Wiki link</a></li>' + |
|
1130 | 1130 | '<li><a href="#Subtitle-with-another-Wiki-link">Subtitle with another Wiki link</a></li>' + |
|
1131 | 1131 | '<li><a href="#Subtitle-with-red-text">Subtitle with red text</a>' + |
|
1132 | 1132 | '<ul>' + |
|
1133 | 1133 | '<li><a href="#Subtitle-with-some-modifiers">Subtitle with some modifiers</a></li>' + |
|
1134 | 1134 | '<li><a href="#Subtitle-with-inline-code">Subtitle with inline code</a></li>' + |
|
1135 | 1135 | '</ul>' + |
|
1136 | 1136 | '</li>' + |
|
1137 | 1137 | '</ul>' + |
|
1138 | 1138 | '</li>' + |
|
1139 | 1139 | '<li><a href="#Another-title">Another title</a>' + |
|
1140 | 1140 | '<ul>' + |
|
1141 | 1141 | '<li>' + |
|
1142 | 1142 | '<ul>' + |
|
1143 | 1143 | '<li><a href="#An-Internet-link-inside-subtitle">An Internet link inside subtitle</a></li>' + |
|
1144 | 1144 | '</ul>' + |
|
1145 | 1145 | '</li>' + |
|
1146 | 1146 | '<li><a href="#Project-Name">Project Name</a></li>' + |
|
1147 | 1147 | '</ul>' + |
|
1148 | 1148 | '</li>' + |
|
1149 | 1149 | '</ul>' |
|
1150 | 1150 | |
|
1151 | 1151 | @project = Project.find(1) |
|
1152 | 1152 | assert textilizable(raw).gsub("\n", "").include?(expected) |
|
1153 | 1153 | end |
|
1154 | 1154 | |
|
1155 | 1155 | def test_table_of_content_should_generate_unique_anchors |
|
1156 | 1156 | raw = <<-RAW |
|
1157 | 1157 | {{toc}} |
|
1158 | 1158 | |
|
1159 | 1159 | h1. Title |
|
1160 | 1160 | |
|
1161 | 1161 | h2. Subtitle |
|
1162 | 1162 | |
|
1163 | 1163 | h2. Subtitle |
|
1164 | 1164 | RAW |
|
1165 | 1165 | |
|
1166 | 1166 | expected = '<ul class="toc">' + |
|
1167 | 1167 | '<li><a href="#Title">Title</a>' + |
|
1168 | 1168 | '<ul>' + |
|
1169 | 1169 | '<li><a href="#Subtitle">Subtitle</a></li>' + |
|
1170 | 1170 | '<li><a href="#Subtitle-2">Subtitle</a></li>' + |
|
1171 | 1171 | '</ul>' + |
|
1172 | 1172 | '</li>' + |
|
1173 | 1173 | '</ul>' |
|
1174 | 1174 | |
|
1175 | 1175 | @project = Project.find(1) |
|
1176 | 1176 | result = textilizable(raw).gsub("\n", "") |
|
1177 | 1177 | assert_include expected, result |
|
1178 | 1178 | assert_include '<a name="Subtitle">', result |
|
1179 | 1179 | assert_include '<a name="Subtitle-2">', result |
|
1180 | 1180 | end |
|
1181 | 1181 | |
|
1182 | 1182 | def test_table_of_content_should_contain_included_page_headings |
|
1183 | 1183 | raw = <<-RAW |
|
1184 | 1184 | {{toc}} |
|
1185 | 1185 | |
|
1186 | 1186 | h1. Included |
|
1187 | 1187 | |
|
1188 | 1188 | {{include(Child_1)}} |
|
1189 | 1189 | RAW |
|
1190 | 1190 | |
|
1191 | 1191 | expected = '<ul class="toc">' + |
|
1192 | 1192 | '<li><a href="#Included">Included</a></li>' + |
|
1193 | 1193 | '<li><a href="#Child-page-1">Child page 1</a></li>' + |
|
1194 | 1194 | '</ul>' |
|
1195 | 1195 | |
|
1196 | 1196 | @project = Project.find(1) |
|
1197 | 1197 | assert textilizable(raw).gsub("\n", "").include?(expected) |
|
1198 | 1198 | end |
|
1199 | 1199 | |
|
1200 | 1200 | def test_toc_with_textile_formatting_should_be_parsed |
|
1201 | 1201 | with_settings :text_formatting => 'textile' do |
|
1202 | 1202 | assert_select_in textilizable("{{toc}}\n\nh1. Heading"), 'ul.toc li', :text => 'Heading' |
|
1203 | 1203 | assert_select_in textilizable("{{<toc}}\n\nh1. Heading"), 'ul.toc.left li', :text => 'Heading' |
|
1204 | 1204 | assert_select_in textilizable("{{>toc}}\n\nh1. Heading"), 'ul.toc.right li', :text => 'Heading' |
|
1205 | 1205 | end |
|
1206 | 1206 | end |
|
1207 | 1207 | |
|
1208 | 1208 | if Object.const_defined?(:Redcarpet) |
|
1209 | 1209 | def test_toc_with_markdown_formatting_should_be_parsed |
|
1210 | 1210 | with_settings :text_formatting => 'markdown' do |
|
1211 | 1211 | assert_select_in textilizable("{{toc}}\n\n# Heading"), 'ul.toc li', :text => 'Heading' |
|
1212 | 1212 | assert_select_in textilizable("{{<toc}}\n\n# Heading"), 'ul.toc.left li', :text => 'Heading' |
|
1213 | 1213 | assert_select_in textilizable("{{>toc}}\n\n# Heading"), 'ul.toc.right li', :text => 'Heading' |
|
1214 | 1214 | end |
|
1215 | 1215 | end |
|
1216 | 1216 | end |
|
1217 | 1217 | |
|
1218 | 1218 | def test_section_edit_links |
|
1219 | 1219 | raw = <<-RAW |
|
1220 | 1220 | h1. Title |
|
1221 | 1221 | |
|
1222 | 1222 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero. |
|
1223 | 1223 | |
|
1224 | 1224 | h2. Subtitle with a [[Wiki]] link |
|
1225 | 1225 | |
|
1226 | 1226 | h2. Subtitle with *some* _modifiers_ |
|
1227 | 1227 | |
|
1228 | 1228 | h2. Subtitle with @inline code@ |
|
1229 | 1229 | |
|
1230 | 1230 | <pre> |
|
1231 | 1231 | some code |
|
1232 | 1232 | |
|
1233 | 1233 | h2. heading inside pre |
|
1234 | 1234 | |
|
1235 | 1235 | <h2>html heading inside pre</h2> |
|
1236 | 1236 | </pre> |
|
1237 | 1237 | |
|
1238 | 1238 | h2. Subtitle after pre tag |
|
1239 | 1239 | RAW |
|
1240 | 1240 | |
|
1241 | 1241 | @project = Project.find(1) |
|
1242 | 1242 | set_language_if_valid 'en' |
|
1243 | 1243 | result = textilizable(raw, :edit_section_links => {:controller => 'wiki', :action => 'edit', :project_id => '1', :id => 'Test'}).gsub("\n", "") |
|
1244 | 1244 | |
|
1245 | 1245 | # heading that contains inline code |
|
1246 | 1246 | assert_match Regexp.new('<div class="contextual heading-2" title="Edit this section" id="section-4">' + |
|
1247 | 1247 | '<a class="icon-only icon-edit" href="/projects/1/wiki/Test/edit\?section=4">Edit this section</a></div>' + |
|
1248 | 1248 | '<a name="Subtitle-with-inline-code"></a>' + |
|
1249 | 1249 | '<h2 >Subtitle with <code>inline code</code><a href="#Subtitle-with-inline-code" class="wiki-anchor">¶</a></h2>'), |
|
1250 | 1250 | result |
|
1251 | 1251 | |
|
1252 | 1252 | # last heading |
|
1253 | 1253 | assert_match Regexp.new('<div class="contextual heading-2" title="Edit this section" id="section-5">' + |
|
1254 | 1254 | '<a class="icon-only icon-edit" href="/projects/1/wiki/Test/edit\?section=5">Edit this section</a></div>' + |
|
1255 | 1255 | '<a name="Subtitle-after-pre-tag"></a>' + |
|
1256 | 1256 | '<h2 >Subtitle after pre tag<a href="#Subtitle-after-pre-tag" class="wiki-anchor">¶</a></h2>'), |
|
1257 | 1257 | result |
|
1258 | 1258 | end |
|
1259 | 1259 | |
|
1260 | 1260 | def test_default_formatter |
|
1261 | 1261 | with_settings :text_formatting => 'unknown' do |
|
1262 | 1262 | text = 'a *link*: http://www.example.net/' |
|
1263 | 1263 | assert_equal '<p>a *link*: <a class="external" href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text) |
|
1264 | 1264 | end |
|
1265 | 1265 | end |
|
1266 | 1266 | |
|
1267 | 1267 | def test_parse_redmine_links_should_handle_a_tag_without_attributes |
|
1268 | 1268 | text = '<a>http://example.com</a>' |
|
1269 | 1269 | expected = text.dup |
|
1270 | 1270 | parse_redmine_links(text, nil, nil, nil, true, {}) |
|
1271 | 1271 | assert_equal expected, text |
|
1272 | 1272 | end |
|
1273 | 1273 | |
|
1274 | 1274 | def test_due_date_distance_in_words |
|
1275 | 1275 | to_test = { Date.today => 'Due in 0 days', |
|
1276 | 1276 | Date.today + 1 => 'Due in 1 day', |
|
1277 | 1277 | Date.today + 100 => 'Due in about 3 months', |
|
1278 | 1278 | Date.today + 20000 => 'Due in over 54 years', |
|
1279 | 1279 | Date.today - 1 => '1 day late', |
|
1280 | 1280 | Date.today - 100 => 'about 3 months late', |
|
1281 | 1281 | Date.today - 20000 => 'over 54 years late', |
|
1282 | 1282 | } |
|
1283 | 1283 | ::I18n.locale = :en |
|
1284 | 1284 | to_test.each do |date, expected| |
|
1285 | 1285 | assert_equal expected, due_date_distance_in_words(date) |
|
1286 | 1286 | end |
|
1287 | 1287 | end |
|
1288 | 1288 | |
|
1289 | 1289 | def test_avatar_enabled |
|
1290 | 1290 | with_settings :gravatar_enabled => '1' do |
|
1291 | 1291 | assert avatar(User.find_by_mail('jsmith@somenet.foo')).include?(Digest::MD5.hexdigest('jsmith@somenet.foo')) |
|
1292 | 1292 | assert avatar('jsmith <jsmith@somenet.foo>').include?(Digest::MD5.hexdigest('jsmith@somenet.foo')) |
|
1293 | 1293 | # Default size is 50 |
|
1294 | 1294 | assert avatar('jsmith <jsmith@somenet.foo>').include?('size=50') |
|
1295 | 1295 | assert avatar('jsmith <jsmith@somenet.foo>', :size => 24).include?('size=24') |
|
1296 | 1296 | # Non-avatar options should be considered html options |
|
1297 | 1297 | assert avatar('jsmith <jsmith@somenet.foo>', :title => 'John Smith').include?('title="John Smith"') |
|
1298 | 1298 | # The default class of the img tag should be gravatar |
|
1299 | 1299 | assert avatar('jsmith <jsmith@somenet.foo>').include?('class="gravatar"') |
|
1300 | 1300 | assert !avatar('jsmith <jsmith@somenet.foo>', :class => 'picture').include?('class="gravatar"') |
|
1301 | 1301 | assert_nil avatar('jsmith') |
|
1302 | 1302 | assert_nil avatar(nil) |
|
1303 | 1303 | end |
|
1304 | 1304 | end |
|
1305 | 1305 | |
|
1306 | 1306 | def test_avatar_disabled |
|
1307 | 1307 | with_settings :gravatar_enabled => '0' do |
|
1308 | 1308 | assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo')) |
|
1309 | 1309 | end |
|
1310 | 1310 | end |
|
1311 | 1311 | |
|
1312 | 1312 | def test_link_to_user |
|
1313 | 1313 | user = User.find(2) |
|
1314 | 1314 | result = link_to("John Smith", "/users/2", :class => "user active") |
|
1315 | 1315 | assert_equal result, link_to_user(user) |
|
1316 | 1316 | end |
|
1317 | 1317 | |
|
1318 | 1318 | def test_link_to_user_should_not_link_to_locked_user |
|
1319 | 1319 | with_current_user nil do |
|
1320 | 1320 | user = User.find(5) |
|
1321 | 1321 | assert user.locked? |
|
1322 | 1322 | assert_equal 'Dave2 Lopper2', link_to_user(user) |
|
1323 | 1323 | end |
|
1324 | 1324 | end |
|
1325 | 1325 | |
|
1326 | 1326 | def test_link_to_user_should_link_to_locked_user_if_current_user_is_admin |
|
1327 | 1327 | with_current_user User.find(1) do |
|
1328 | 1328 | user = User.find(5) |
|
1329 | 1329 | assert user.locked? |
|
1330 | 1330 | result = link_to("Dave2 Lopper2", "/users/5", :class => "user locked") |
|
1331 | 1331 | assert_equal result, link_to_user(user) |
|
1332 | 1332 | end |
|
1333 | 1333 | end |
|
1334 | 1334 | |
|
1335 | 1335 | def test_link_to_user_should_not_link_to_anonymous |
|
1336 | 1336 | user = User.anonymous |
|
1337 | 1337 | assert user.anonymous? |
|
1338 | 1338 | t = link_to_user(user) |
|
1339 | 1339 | assert_equal ::I18n.t(:label_user_anonymous), t |
|
1340 | 1340 | end |
|
1341 | 1341 | |
|
1342 | 1342 | def test_link_to_attachment |
|
1343 | 1343 | a = Attachment.find(3) |
|
1344 | 1344 | assert_equal '<a href="/attachments/3/logo.gif">logo.gif</a>', |
|
1345 | 1345 | link_to_attachment(a) |
|
1346 | 1346 | assert_equal '<a href="/attachments/3/logo.gif">Text</a>', |
|
1347 | 1347 | link_to_attachment(a, :text => 'Text') |
|
1348 | 1348 | result = link_to("logo.gif", "/attachments/3/logo.gif", :class => "foo") |
|
1349 | 1349 | assert_equal result, |
|
1350 | 1350 | link_to_attachment(a, :class => 'foo') |
|
1351 | 1351 | assert_equal '<a href="/attachments/download/3/logo.gif">logo.gif</a>', |
|
1352 | 1352 | link_to_attachment(a, :download => true) |
|
1353 | 1353 | assert_equal '<a href="http://test.host/attachments/3/logo.gif">logo.gif</a>', |
|
1354 | 1354 | link_to_attachment(a, :only_path => false) |
|
1355 | 1355 | end |
|
1356 | 1356 | |
|
1357 | 1357 | def test_thumbnail_tag |
|
1358 | 1358 | a = Attachment.find(3) |
|
1359 | 1359 | assert_select_in thumbnail_tag(a), |
|
1360 | 1360 | 'a[href=?][title=?] img[alt="3"][src=?]', |
|
1361 | 1361 | "/attachments/3/logo.gif", "logo.gif", "/attachments/thumbnail/3" |
|
1362 | 1362 | end |
|
1363 | 1363 | |
|
1364 | 1364 | def test_link_to_project |
|
1365 | 1365 | project = Project.find(1) |
|
1366 | 1366 | assert_equal %(<a href="/projects/ecookbook">eCookbook</a>), |
|
1367 | 1367 | link_to_project(project) |
|
1368 | 1368 | assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>), |
|
1369 | 1369 | link_to_project(project, {:only_path => false, :jump => 'blah'}) |
|
1370 | 1370 | end |
|
1371 | 1371 | |
|
1372 | 1372 | def test_link_to_project_settings |
|
1373 | 1373 | project = Project.find(1) |
|
1374 | 1374 | assert_equal '<a href="/projects/ecookbook/settings">eCookbook</a>', link_to_project_settings(project) |
|
1375 | 1375 | |
|
1376 | 1376 | project.status = Project::STATUS_CLOSED |
|
1377 | 1377 | assert_equal '<a href="/projects/ecookbook">eCookbook</a>', link_to_project_settings(project) |
|
1378 | 1378 | |
|
1379 | 1379 | project.status = Project::STATUS_ARCHIVED |
|
1380 | 1380 | assert_equal 'eCookbook', link_to_project_settings(project) |
|
1381 | 1381 | end |
|
1382 | 1382 | |
|
1383 | 1383 | def test_link_to_legacy_project_with_numerical_identifier_should_use_id |
|
1384 | 1384 | # numeric identifier are no longer allowed |
|
1385 | 1385 | Project.where(:id => 1).update_all(:identifier => 25) |
|
1386 | 1386 | assert_equal '<a href="/projects/1">eCookbook</a>', |
|
1387 | 1387 | link_to_project(Project.find(1)) |
|
1388 | 1388 | end |
|
1389 | 1389 | |
|
1390 | 1390 | def test_principals_options_for_select_with_users |
|
1391 | 1391 | User.current = nil |
|
1392 | 1392 | users = [User.find(2), User.find(4)] |
|
1393 | 1393 | assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>), |
|
1394 | 1394 | principals_options_for_select(users) |
|
1395 | 1395 | end |
|
1396 | 1396 | |
|
1397 | 1397 | def test_principals_options_for_select_with_selected |
|
1398 | 1398 | User.current = nil |
|
1399 | 1399 | users = [User.find(2), User.find(4)] |
|
1400 | 1400 | assert_equal %(<option value="2">John Smith</option><option value="4" selected="selected">Robert Hill</option>), |
|
1401 | 1401 | principals_options_for_select(users, User.find(4)) |
|
1402 | 1402 | end |
|
1403 | 1403 | |
|
1404 | 1404 | def test_principals_options_for_select_with_users_and_groups |
|
1405 | 1405 | User.current = nil |
|
1406 | 1406 | set_language_if_valid 'en' |
|
1407 | 1407 | users = [User.find(2), Group.find(11), User.find(4), Group.find(10)] |
|
1408 | 1408 | assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>) + |
|
1409 | 1409 | %(<optgroup label="Groups"><option value="10">A Team</option><option value="11">B Team</option></optgroup>), |
|
1410 | 1410 | principals_options_for_select(users) |
|
1411 | 1411 | end |
|
1412 | 1412 | |
|
1413 | 1413 | def test_principals_options_for_select_with_empty_collection |
|
1414 | 1414 | assert_equal '', principals_options_for_select([]) |
|
1415 | 1415 | end |
|
1416 | 1416 | |
|
1417 | 1417 | def test_principals_options_for_select_should_include_me_option_when_current_user_is_in_collection |
|
1418 | 1418 | set_language_if_valid 'en' |
|
1419 | 1419 | users = [User.find(2), User.find(4)] |
|
1420 | 1420 | User.current = User.find(4) |
|
1421 | 1421 | assert_include '<option value="4"><< me >></option>', principals_options_for_select(users) |
|
1422 | 1422 | end |
|
1423 | 1423 | |
|
1424 | 1424 | def test_stylesheet_link_tag_should_pick_the_default_stylesheet |
|
1425 | 1425 | assert_match 'href="/stylesheets/styles.css"', stylesheet_link_tag("styles") |
|
1426 | 1426 | end |
|
1427 | 1427 | |
|
1428 | 1428 | def test_stylesheet_link_tag_for_plugin_should_pick_the_plugin_stylesheet |
|
1429 | 1429 | assert_match 'href="/plugin_assets/foo/stylesheets/styles.css"', stylesheet_link_tag("styles", :plugin => :foo) |
|
1430 | 1430 | end |
|
1431 | 1431 | |
|
1432 | 1432 | def test_image_tag_should_pick_the_default_image |
|
1433 | 1433 | assert_match 'src="/images/image.png"', image_tag("image.png") |
|
1434 | 1434 | end |
|
1435 | 1435 | |
|
1436 | 1436 | def test_image_tag_should_pick_the_theme_image_if_it_exists |
|
1437 | 1437 | theme = Redmine::Themes.themes.last |
|
1438 | 1438 | theme.images << 'image.png' |
|
1439 | 1439 | |
|
1440 | 1440 | with_settings :ui_theme => theme.id do |
|
1441 | 1441 | assert_match %|src="/themes/#{theme.dir}/images/image.png"|, image_tag("image.png") |
|
1442 | 1442 | assert_match %|src="/images/other.png"|, image_tag("other.png") |
|
1443 | 1443 | end |
|
1444 | 1444 | ensure |
|
1445 | 1445 | theme.images.delete 'image.png' |
|
1446 | 1446 | end |
|
1447 | 1447 | |
|
1448 | 1448 | def test_image_tag_sfor_plugin_should_pick_the_plugin_image |
|
1449 | 1449 | assert_match 'src="/plugin_assets/foo/images/image.png"', image_tag("image.png", :plugin => :foo) |
|
1450 | 1450 | end |
|
1451 | 1451 | |
|
1452 | 1452 | def test_javascript_include_tag_should_pick_the_default_javascript |
|
1453 | 1453 | assert_match 'src="/javascripts/scripts.js"', javascript_include_tag("scripts") |
|
1454 | 1454 | end |
|
1455 | 1455 | |
|
1456 | 1456 | def test_javascript_include_tag_for_plugin_should_pick_the_plugin_javascript |
|
1457 | 1457 | assert_match 'src="/plugin_assets/foo/javascripts/scripts.js"', javascript_include_tag("scripts", :plugin => :foo) |
|
1458 | 1458 | end |
|
1459 | 1459 | |
|
1460 | 1460 | def test_raw_json_should_escape_closing_tags |
|
1461 | 1461 | s = raw_json(["<foo>bar</foo>"]) |
|
1462 | 1462 | assert_include '\/foo', s |
|
1463 | 1463 | end |
|
1464 | 1464 | |
|
1465 | 1465 | def test_raw_json_should_be_html_safe |
|
1466 | 1466 | s = raw_json(["foo"]) |
|
1467 | 1467 | assert s.html_safe? |
|
1468 | 1468 | end |
|
1469 | 1469 | |
|
1470 | 1470 | def test_html_title_should_app_title_if_not_set |
|
1471 | 1471 | assert_equal 'Redmine', html_title |
|
1472 | 1472 | end |
|
1473 | 1473 | |
|
1474 | 1474 | def test_html_title_should_join_items |
|
1475 | 1475 | html_title 'Foo', 'Bar' |
|
1476 | 1476 | assert_equal 'Foo - Bar - Redmine', html_title |
|
1477 | 1477 | end |
|
1478 | 1478 | |
|
1479 | 1479 | def test_html_title_should_append_current_project_name |
|
1480 | 1480 | @project = Project.find(1) |
|
1481 | 1481 | html_title 'Foo', 'Bar' |
|
1482 | 1482 | assert_equal 'Foo - Bar - eCookbook - Redmine', html_title |
|
1483 | 1483 | end |
|
1484 | 1484 | |
|
1485 | 1485 | def test_title_should_return_a_h2_tag |
|
1486 | 1486 | assert_equal '<h2>Foo</h2>', title('Foo') |
|
1487 | 1487 | end |
|
1488 | 1488 | |
|
1489 | 1489 | def test_title_should_set_html_title |
|
1490 | 1490 | title('Foo') |
|
1491 | 1491 | assert_equal 'Foo - Redmine', html_title |
|
1492 | 1492 | end |
|
1493 | 1493 | |
|
1494 | 1494 | def test_title_should_turn_arrays_into_links |
|
1495 | 1495 | assert_equal '<h2><a href="/foo">Foo</a></h2>', title(['Foo', '/foo']) |
|
1496 | 1496 | assert_equal 'Foo - Redmine', html_title |
|
1497 | 1497 | end |
|
1498 | 1498 | |
|
1499 | 1499 | def test_title_should_join_items |
|
1500 | 1500 | assert_equal '<h2>Foo » Bar</h2>', title('Foo', 'Bar') |
|
1501 | 1501 | assert_equal 'Bar - Foo - Redmine', html_title |
|
1502 | 1502 | end |
|
1503 | 1503 | |
|
1504 | 1504 | def test_favicon_path |
|
1505 | 1505 | assert_match %r{^/favicon\.ico}, favicon_path |
|
1506 | 1506 | end |
|
1507 | 1507 | |
|
1508 | 1508 | def test_favicon_path_with_suburi |
|
1509 | 1509 | Redmine::Utils.relative_url_root = '/foo' |
|
1510 | 1510 | assert_match %r{^/foo/favicon\.ico}, favicon_path |
|
1511 | 1511 | ensure |
|
1512 | 1512 | Redmine::Utils.relative_url_root = '' |
|
1513 | 1513 | end |
|
1514 | 1514 | |
|
1515 | 1515 | def test_favicon_url |
|
1516 | 1516 | assert_match %r{^http://test\.host/favicon\.ico}, favicon_url |
|
1517 | 1517 | end |
|
1518 | 1518 | |
|
1519 | 1519 | def test_favicon_url_with_suburi |
|
1520 | 1520 | Redmine::Utils.relative_url_root = '/foo' |
|
1521 | 1521 | assert_match %r{^http://test\.host/foo/favicon\.ico}, favicon_url |
|
1522 | 1522 | ensure |
|
1523 | 1523 | Redmine::Utils.relative_url_root = '' |
|
1524 | 1524 | end |
|
1525 | 1525 | |
|
1526 | 1526 | def test_truncate_single_line |
|
1527 | 1527 | str = "01234" |
|
1528 | 1528 | result = truncate_single_line_raw("#{str}\n#{str}", 10) |
|
1529 | 1529 | assert_equal "01234 0...", result |
|
1530 | 1530 | assert !result.html_safe? |
|
1531 | 1531 | result = truncate_single_line_raw("#{str}<&#>\n#{str}#{str}", 16) |
|
1532 | 1532 | assert_equal "01234<&#> 012...", result |
|
1533 | 1533 | assert !result.html_safe? |
|
1534 | 1534 | end |
|
1535 | 1535 | |
|
1536 | 1536 | def test_truncate_single_line_non_ascii |
|
1537 | 1537 | ja = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e".force_encoding('UTF-8') |
|
1538 | 1538 | result = truncate_single_line_raw("#{ja}\n#{ja}\n#{ja}", 10) |
|
1539 | 1539 | assert_equal "#{ja} #{ja}...", result |
|
1540 | 1540 | assert !result.html_safe? |
|
1541 | 1541 | end |
|
1542 | 1542 | |
|
1543 | 1543 | def test_back_url_should_remove_utf8_checkmark_from_referer |
|
1544 | 1544 | stubs(:request).returns(stub(:env => {'HTTP_REFERER' => "/path?utf8=\u2713&foo=bar"})) |
|
1545 | 1545 | assert_equal "/path?foo=bar", back_url |
|
1546 | 1546 | end |
|
1547 | ||
|
1548 | def test_hours_formatting | |
|
1549 | set_language_if_valid 'en' | |
|
1550 | ||
|
1551 | with_settings :timespan_format => 'minutes' do | |
|
1552 | assert_equal '0:45', format_hours(0.75) | |
|
1553 | assert_equal '0:45 h', l_hours_short(0.75) | |
|
1554 | assert_equal '0:45 hour', l_hours(0.75) | |
|
1555 | end | |
|
1556 | with_settings :timespan_format => 'decimal' do | |
|
1557 | assert_equal '0.75', format_hours(0.75) | |
|
1558 | assert_equal '0.75 h', l_hours_short(0.75) | |
|
1559 | assert_equal '0.75 hour', l_hours(0.75) | |
|
1560 | end | |
|
1561 | end | |
|
1562 | ||
|
1563 | def test_html_hours | |
|
1564 | assert_equal '<span class="hours hours-int">0</span><span class="hours hours-dec">:45</span>', html_hours('0:45') | |
|
1565 | assert_equal '<span class="hours hours-int">0</span><span class="hours hours-dec">.75</span>', html_hours('0.75') | |
|
1566 | end | |
|
1547 | 1567 | end |
General Comments 0
You need to be logged in to leave comments.
Login now