##// END OF EJS Templates
Removed TabularFormBuilder references in views....
Jean-Philippe Lang -
r8022:ede9a03405fb
parent child
Show More
@@ -1,1066 +1,1080
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2011 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
28 28 extend Forwardable
29 29 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
30 30
31 31 # Return true if user is authorized for controller/action, otherwise false
32 32 def authorize_for(controller, action)
33 33 User.current.allowed_to?({:controller => controller, :action => action}, @project)
34 34 end
35 35
36 36 # Display a link if user is authorized
37 37 #
38 38 # @param [String] name Anchor text (passed to link_to)
39 39 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
40 40 # @param [optional, Hash] html_options Options passed to link_to
41 41 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
42 42 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
43 43 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
44 44 end
45 45
46 46 # Display a link to remote if user is authorized
47 47 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
48 48 url = options[:url] || {}
49 49 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
50 50 end
51 51
52 52 # Displays a link to user's account page if active
53 53 def link_to_user(user, options={})
54 54 if user.is_a?(User)
55 55 name = h(user.name(options[:format]))
56 56 if user.active?
57 57 link_to name, :controller => 'users', :action => 'show', :id => user
58 58 else
59 59 name
60 60 end
61 61 else
62 62 h(user.to_s)
63 63 end
64 64 end
65 65
66 66 # Displays a link to +issue+ with its subject.
67 67 # Examples:
68 68 #
69 69 # link_to_issue(issue) # => Defect #6: This is the subject
70 70 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
71 71 # link_to_issue(issue, :subject => false) # => Defect #6
72 72 # link_to_issue(issue, :project => true) # => Foo - Defect #6
73 73 #
74 74 def link_to_issue(issue, options={})
75 75 title = nil
76 76 subject = nil
77 77 if options[:subject] == false
78 78 title = truncate(issue.subject, :length => 60)
79 79 else
80 80 subject = issue.subject
81 81 if options[:truncate]
82 82 subject = truncate(subject, :length => options[:truncate])
83 83 end
84 84 end
85 85 s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
86 86 :class => issue.css_classes,
87 87 :title => title
88 88 s << ": #{h subject}" if subject
89 89 s = "#{h issue.project} - " + s if options[:project]
90 90 s
91 91 end
92 92
93 93 # Generates a link to an attachment.
94 94 # Options:
95 95 # * :text - Link text (default to attachment filename)
96 96 # * :download - Force download (default: false)
97 97 def link_to_attachment(attachment, options={})
98 98 text = options.delete(:text) || attachment.filename
99 99 action = options.delete(:download) ? 'download' : 'show'
100 100 link_to(h(text),
101 101 {:controller => 'attachments', :action => action,
102 102 :id => attachment, :filename => attachment.filename },
103 103 options)
104 104 end
105 105
106 106 # Generates a link to a SCM revision
107 107 # Options:
108 108 # * :text - Link text (default to the formatted revision)
109 109 def link_to_revision(revision, project, options={})
110 110 text = options.delete(:text) || format_revision(revision)
111 111 rev = revision.respond_to?(:identifier) ? revision.identifier : revision
112 112 link_to(
113 113 h(text),
114 114 {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
115 115 :title => l(:label_revision_id, format_revision(revision))
116 116 )
117 117 end
118 118
119 119 # Generates a link to a message
120 120 def link_to_message(message, options={}, html_options = nil)
121 121 link_to(
122 122 h(truncate(message.subject, :length => 60)),
123 123 { :controller => 'messages', :action => 'show',
124 124 :board_id => message.board_id,
125 125 :id => message.root,
126 126 :r => (message.parent_id && message.id),
127 127 :anchor => (message.parent_id ? "message-#{message.id}" : nil)
128 128 }.merge(options),
129 129 html_options
130 130 )
131 131 end
132 132
133 133 # Generates a link to a project if active
134 134 # Examples:
135 135 #
136 136 # link_to_project(project) # => link to the specified project overview
137 137 # link_to_project(project, :action=>'settings') # => link to project settings
138 138 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
139 139 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
140 140 #
141 141 def link_to_project(project, options={}, html_options = nil)
142 142 if project.active?
143 143 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
144 144 link_to(h(project), url, html_options)
145 145 else
146 146 h(project)
147 147 end
148 148 end
149 149
150 150 def toggle_link(name, id, options={})
151 151 onclick = "Element.toggle('#{id}'); "
152 152 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
153 153 onclick << "return false;"
154 154 link_to(name, "#", :onclick => onclick)
155 155 end
156 156
157 157 def image_to_function(name, function, html_options = {})
158 158 html_options.symbolize_keys!
159 159 tag(:input, html_options.merge({
160 160 :type => "image", :src => image_path(name),
161 161 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
162 162 }))
163 163 end
164 164
165 165 def prompt_to_remote(name, text, param, url, html_options = {})
166 166 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
167 167 link_to name, {}, html_options
168 168 end
169 169
170 170 def format_activity_title(text)
171 171 h(truncate_single_line(text, :length => 100))
172 172 end
173 173
174 174 def format_activity_day(date)
175 175 date == Date.today ? l(:label_today).titleize : format_date(date)
176 176 end
177 177
178 178 def format_activity_description(text)
179 179 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')
180 180 ).gsub(/[\r\n]+/, "<br />").html_safe
181 181 end
182 182
183 183 def format_version_name(version)
184 184 if version.project == @project
185 185 h(version)
186 186 else
187 187 h("#{version.project} - #{version}")
188 188 end
189 189 end
190 190
191 191 def due_date_distance_in_words(date)
192 192 if date
193 193 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
194 194 end
195 195 end
196 196
197 197 def render_page_hierarchy(pages, node=nil, options={})
198 198 content = ''
199 199 if pages[node]
200 200 content << "<ul class=\"pages-hierarchy\">\n"
201 201 pages[node].each do |page|
202 202 content << "<li>"
203 203 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
204 204 :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
205 205 content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
206 206 content << "</li>\n"
207 207 end
208 208 content << "</ul>\n"
209 209 end
210 210 content.html_safe
211 211 end
212 212
213 213 # Renders flash messages
214 214 def render_flash_messages
215 215 s = ''
216 216 flash.each do |k,v|
217 217 s << (content_tag('div', v.html_safe, :class => "flash #{k}"))
218 218 end
219 219 s.html_safe
220 220 end
221 221
222 222 # Renders tabs and their content
223 223 def render_tabs(tabs)
224 224 if tabs.any?
225 225 render :partial => 'common/tabs', :locals => {:tabs => tabs}
226 226 else
227 227 content_tag 'p', l(:label_no_data), :class => "nodata"
228 228 end
229 229 end
230 230
231 231 # Renders the project quick-jump box
232 232 def render_project_jump_box
233 233 return unless User.current.logged?
234 234 projects = User.current.memberships.collect(&:project).compact.uniq
235 235 if projects.any?
236 236 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
237 237 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
238 238 '<option value="" disabled="disabled">---</option>'
239 239 s << project_tree_options_for_select(projects, :selected => @project) do |p|
240 240 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
241 241 end
242 242 s << '</select>'
243 243 s.html_safe
244 244 end
245 245 end
246 246
247 247 def project_tree_options_for_select(projects, options = {})
248 248 s = ''
249 249 project_tree(projects) do |project, level|
250 250 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
251 251 tag_options = {:value => project.id}
252 252 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
253 253 tag_options[:selected] = 'selected'
254 254 else
255 255 tag_options[:selected] = nil
256 256 end
257 257 tag_options.merge!(yield(project)) if block_given?
258 258 s << content_tag('option', name_prefix + h(project), tag_options)
259 259 end
260 260 s.html_safe
261 261 end
262 262
263 263 # Yields the given block for each project with its level in the tree
264 264 #
265 265 # Wrapper for Project#project_tree
266 266 def project_tree(projects, &block)
267 267 Project.project_tree(projects, &block)
268 268 end
269 269
270 270 def project_nested_ul(projects, &block)
271 271 s = ''
272 272 if projects.any?
273 273 ancestors = []
274 274 projects.sort_by(&:lft).each do |project|
275 275 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
276 276 s << "<ul>\n"
277 277 else
278 278 ancestors.pop
279 279 s << "</li>"
280 280 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
281 281 ancestors.pop
282 282 s << "</ul></li>\n"
283 283 end
284 284 end
285 285 s << "<li>"
286 286 s << yield(project).to_s
287 287 ancestors << project
288 288 end
289 289 s << ("</li></ul>\n" * ancestors.size)
290 290 end
291 291 s.html_safe
292 292 end
293 293
294 294 def principals_check_box_tags(name, principals)
295 295 s = ''
296 296 principals.sort.each do |principal|
297 297 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
298 298 end
299 299 s.html_safe
300 300 end
301 301
302 302 # Returns a string for users/groups option tags
303 303 def principals_options_for_select(collection, selected=nil)
304 304 s = ''
305 305 groups = ''
306 306 collection.sort.each do |element|
307 307 selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
308 308 (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
309 309 end
310 310 unless groups.empty?
311 311 s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
312 312 end
313 313 s
314 314 end
315 315
316 316 # Truncates and returns the string as a single line
317 317 def truncate_single_line(string, *args)
318 318 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
319 319 end
320 320
321 321 # Truncates at line break after 250 characters or options[:length]
322 322 def truncate_lines(string, options={})
323 323 length = options[:length] || 250
324 324 if string.to_s =~ /\A(.{#{length}}.*?)$/m
325 325 "#{$1}..."
326 326 else
327 327 string
328 328 end
329 329 end
330 330
331 331 def html_hours(text)
332 332 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
333 333 end
334 334
335 335 def authoring(created, author, options={})
336 336 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
337 337 end
338 338
339 339 def time_tag(time)
340 340 text = distance_of_time_in_words(Time.now, time)
341 341 if @project
342 342 link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
343 343 else
344 344 content_tag('acronym', text, :title => format_time(time))
345 345 end
346 346 end
347 347
348 348 def syntax_highlight(name, content)
349 349 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
350 350 end
351 351
352 352 def to_path_param(path)
353 353 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
354 354 end
355 355
356 356 def pagination_links_full(paginator, count=nil, options={})
357 357 page_param = options.delete(:page_param) || :page
358 358 per_page_links = options.delete(:per_page_links)
359 359 url_param = params.dup
360 360
361 361 html = ''
362 362 if paginator.current.previous
363 363 # \xc2\xab(utf-8) = &#171;
364 364 html << link_to_content_update(
365 365 "\xc2\xab " + l(:label_previous),
366 366 url_param.merge(page_param => paginator.current.previous)) + ' '
367 367 end
368 368
369 369 html << (pagination_links_each(paginator, options) do |n|
370 370 link_to_content_update(n.to_s, url_param.merge(page_param => n))
371 371 end || '')
372 372
373 373 if paginator.current.next
374 374 # \xc2\xbb(utf-8) = &#187;
375 375 html << ' ' + link_to_content_update(
376 376 (l(:label_next) + " \xc2\xbb"),
377 377 url_param.merge(page_param => paginator.current.next))
378 378 end
379 379
380 380 unless count.nil?
381 381 html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
382 382 if per_page_links != false && links = per_page_links(paginator.items_per_page)
383 383 html << " | #{links}"
384 384 end
385 385 end
386 386
387 387 html.html_safe
388 388 end
389 389
390 390 def per_page_links(selected=nil)
391 391 links = Setting.per_page_options_array.collect do |n|
392 392 n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
393 393 end
394 394 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
395 395 end
396 396
397 397 def reorder_links(name, url, method = :post)
398 398 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
399 399 url.merge({"#{name}[move_to]" => 'highest'}),
400 400 :method => method, :title => l(:label_sort_highest)) +
401 401 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
402 402 url.merge({"#{name}[move_to]" => 'higher'}),
403 403 :method => method, :title => l(:label_sort_higher)) +
404 404 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
405 405 url.merge({"#{name}[move_to]" => 'lower'}),
406 406 :method => method, :title => l(:label_sort_lower)) +
407 407 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
408 408 url.merge({"#{name}[move_to]" => 'lowest'}),
409 409 :method => method, :title => l(:label_sort_lowest))
410 410 end
411 411
412 412 def breadcrumb(*args)
413 413 elements = args.flatten
414 414 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
415 415 end
416 416
417 417 def other_formats_links(&block)
418 418 concat('<p class="other-formats">'.html_safe + l(:label_export_to))
419 419 yield Redmine::Views::OtherFormatsBuilder.new(self)
420 420 concat('</p>'.html_safe)
421 421 end
422 422
423 423 def page_header_title
424 424 if @project.nil? || @project.new_record?
425 425 h(Setting.app_title)
426 426 else
427 427 b = []
428 428 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
429 429 if ancestors.any?
430 430 root = ancestors.shift
431 431 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
432 432 if ancestors.size > 2
433 433 b << "\xe2\x80\xa6"
434 434 ancestors = ancestors[-2, 2]
435 435 end
436 436 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
437 437 end
438 438 b << h(@project)
439 439 b.join(" \xc2\xbb ").html_safe
440 440 end
441 441 end
442 442
443 443 def html_title(*args)
444 444 if args.empty?
445 445 title = @html_title || []
446 446 title << @project.name if @project
447 447 title << Setting.app_title unless Setting.app_title == title.last
448 448 title.select {|t| !t.blank? }.join(' - ')
449 449 else
450 450 @html_title ||= []
451 451 @html_title += args
452 452 end
453 453 end
454 454
455 455 # Returns the theme, controller name, and action as css classes for the
456 456 # HTML body.
457 457 def body_css_classes
458 458 css = []
459 459 if theme = Redmine::Themes.theme(Setting.ui_theme)
460 460 css << 'theme-' + theme.name
461 461 end
462 462
463 463 css << 'controller-' + params[:controller]
464 464 css << 'action-' + params[:action]
465 465 css.join(' ')
466 466 end
467 467
468 468 def accesskey(s)
469 469 Redmine::AccessKeys.key_for s
470 470 end
471 471
472 472 # Formats text according to system settings.
473 473 # 2 ways to call this method:
474 474 # * with a String: textilizable(text, options)
475 475 # * with an object and one of its attribute: textilizable(issue, :description, options)
476 476 def textilizable(*args)
477 477 options = args.last.is_a?(Hash) ? args.pop : {}
478 478 case args.size
479 479 when 1
480 480 obj = options[:object]
481 481 text = args.shift
482 482 when 2
483 483 obj = args.shift
484 484 attr = args.shift
485 485 text = obj.send(attr).to_s
486 486 else
487 487 raise ArgumentError, 'invalid arguments to textilizable'
488 488 end
489 489 return '' if text.blank?
490 490 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
491 491 only_path = options.delete(:only_path) == false ? false : true
492 492
493 493 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
494 494
495 495 @parsed_headings = []
496 496 @current_section = 0 if options[:edit_section_links]
497 497 text = parse_non_pre_blocks(text) do |text|
498 498 [:parse_sections, :parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_macros, :parse_headings].each do |method_name|
499 499 send method_name, text, project, obj, attr, only_path, options
500 500 end
501 501 end
502 502
503 503 if @parsed_headings.any?
504 504 replace_toc(text, @parsed_headings)
505 505 end
506 506
507 507 text
508 508 end
509 509
510 510 def parse_non_pre_blocks(text)
511 511 s = StringScanner.new(text)
512 512 tags = []
513 513 parsed = ''
514 514 while !s.eos?
515 515 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
516 516 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
517 517 if tags.empty?
518 518 yield text
519 519 end
520 520 parsed << text
521 521 if tag
522 522 if closing
523 523 if tags.last == tag.downcase
524 524 tags.pop
525 525 end
526 526 else
527 527 tags << tag.downcase
528 528 end
529 529 parsed << full_tag
530 530 end
531 531 end
532 532 # Close any non closing tags
533 533 while tag = tags.pop
534 534 parsed << "</#{tag}>"
535 535 end
536 536 parsed.html_safe
537 537 end
538 538
539 539 def parse_inline_attachments(text, project, obj, attr, only_path, options)
540 540 # when using an image link, try to use an attachment, if possible
541 541 if options[:attachments] || (obj && obj.respond_to?(:attachments))
542 542 attachments = options[:attachments] || obj.attachments
543 543 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
544 544 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
545 545 # search for the picture in attachments
546 546 if found = Attachment.latest_attach(attachments, filename)
547 547 image_url = url_for :only_path => only_path, :controller => 'attachments',
548 548 :action => 'download', :id => found
549 549 desc = found.description.to_s.gsub('"', '')
550 550 if !desc.blank? && alttext.blank?
551 551 alt = " title=\"#{desc}\" alt=\"#{desc}\""
552 552 end
553 553 "src=\"#{image_url}\"#{alt}".html_safe
554 554 else
555 555 m.html_safe
556 556 end
557 557 end
558 558 end
559 559 end
560 560
561 561 # Wiki links
562 562 #
563 563 # Examples:
564 564 # [[mypage]]
565 565 # [[mypage|mytext]]
566 566 # wiki links can refer other project wikis, using project name or identifier:
567 567 # [[project:]] -> wiki starting page
568 568 # [[project:|mytext]]
569 569 # [[project:mypage]]
570 570 # [[project:mypage|mytext]]
571 571 def parse_wiki_links(text, project, obj, attr, only_path, options)
572 572 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
573 573 link_project = project
574 574 esc, all, page, title = $1, $2, $3, $5
575 575 if esc.nil?
576 576 if page =~ /^([^\:]+)\:(.*)$/
577 577 link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
578 578 page = $2
579 579 title ||= $1 if page.blank?
580 580 end
581 581
582 582 if link_project && link_project.wiki
583 583 # extract anchor
584 584 anchor = nil
585 585 if page =~ /^(.+?)\#(.+)$/
586 586 page, anchor = $1, $2
587 587 end
588 588 anchor = sanitize_anchor_name(anchor) if anchor.present?
589 589 # check if page exists
590 590 wiki_page = link_project.wiki.find_page(page)
591 591 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
592 592 "##{anchor}"
593 593 else
594 594 case options[:wiki_links]
595 595 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
596 596 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
597 597 else
598 598 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
599 599 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
600 600 end
601 601 end
602 602 link_to(title.present? ? title.html_safe : h(page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
603 603 else
604 604 # project or wiki doesn't exist
605 605 all.html_safe
606 606 end
607 607 else
608 608 all.html_safe
609 609 end
610 610 end
611 611 end
612 612
613 613 # Redmine links
614 614 #
615 615 # Examples:
616 616 # Issues:
617 617 # #52 -> Link to issue #52
618 618 # Changesets:
619 619 # r52 -> Link to revision 52
620 620 # commit:a85130f -> Link to scmid starting with a85130f
621 621 # Documents:
622 622 # document#17 -> Link to document with id 17
623 623 # document:Greetings -> Link to the document with title "Greetings"
624 624 # document:"Some document" -> Link to the document with title "Some document"
625 625 # Versions:
626 626 # version#3 -> Link to version with id 3
627 627 # version:1.0.0 -> Link to version named "1.0.0"
628 628 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
629 629 # Attachments:
630 630 # attachment:file.zip -> Link to the attachment of the current object named file.zip
631 631 # Source files:
632 632 # source:some/file -> Link to the file located at /some/file in the project's repository
633 633 # source:some/file@52 -> Link to the file's revision 52
634 634 # source:some/file#L120 -> Link to line 120 of the file
635 635 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
636 636 # export:some/file -> Force the download of the file
637 637 # Forum messages:
638 638 # message#1218 -> Link to message with id 1218
639 639 #
640 640 # Links can refer other objects from other projects, using project identifier:
641 641 # identifier:r52
642 642 # identifier:document:"Some document"
643 643 # identifier:version:1.0.0
644 644 # identifier:source:some/file
645 645 def parse_redmine_links(text, project, obj, attr, only_path, options)
646 646 text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|forum|news|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
647 647 leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
648 648 link = nil
649 649 if project_identifier
650 650 project = Project.visible.find_by_identifier(project_identifier)
651 651 end
652 652 if esc.nil?
653 653 if prefix.nil? && sep == 'r'
654 654 # project.changesets.visible raises an SQL error because of a double join on repositories
655 655 if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
656 656 link = link_to(h("#{project_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
657 657 :class => 'changeset',
658 658 :title => truncate_single_line(changeset.comments, :length => 100))
659 659 end
660 660 elsif sep == '#'
661 661 oid = identifier.to_i
662 662 case prefix
663 663 when nil
664 664 if issue = Issue.visible.find_by_id(oid, :include => :status)
665 665 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
666 666 :class => issue.css_classes,
667 667 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
668 668 end
669 669 when 'document'
670 670 if document = Document.visible.find_by_id(oid)
671 671 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
672 672 :class => 'document'
673 673 end
674 674 when 'version'
675 675 if version = Version.visible.find_by_id(oid)
676 676 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
677 677 :class => 'version'
678 678 end
679 679 when 'message'
680 680 if message = Message.visible.find_by_id(oid, :include => :parent)
681 681 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
682 682 end
683 683 when 'forum'
684 684 if board = Board.visible.find_by_id(oid)
685 685 link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
686 686 :class => 'board'
687 687 end
688 688 when 'news'
689 689 if news = News.visible.find_by_id(oid)
690 690 link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
691 691 :class => 'news'
692 692 end
693 693 when 'project'
694 694 if p = Project.visible.find_by_id(oid)
695 695 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
696 696 end
697 697 end
698 698 elsif sep == ':'
699 699 # removes the double quotes if any
700 700 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
701 701 case prefix
702 702 when 'document'
703 703 if project && document = project.documents.visible.find_by_title(name)
704 704 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
705 705 :class => 'document'
706 706 end
707 707 when 'version'
708 708 if project && version = project.versions.visible.find_by_name(name)
709 709 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
710 710 :class => 'version'
711 711 end
712 712 when 'forum'
713 713 if project && board = project.boards.visible.find_by_name(name)
714 714 link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
715 715 :class => 'board'
716 716 end
717 717 when 'news'
718 718 if project && news = project.news.visible.find_by_title(name)
719 719 link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
720 720 :class => 'news'
721 721 end
722 722 when 'commit'
723 723 if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
724 724 link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
725 725 :class => 'changeset',
726 726 :title => truncate_single_line(h(changeset.comments), :length => 100)
727 727 end
728 728 when 'source', 'export'
729 729 if project && project.repository && User.current.allowed_to?(:browse_repository, project)
730 730 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
731 731 path, rev, anchor = $1, $3, $5
732 732 link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
733 733 :path => to_path_param(path),
734 734 :rev => rev,
735 735 :anchor => anchor,
736 736 :format => (prefix == 'export' ? 'raw' : nil)},
737 737 :class => (prefix == 'export' ? 'source download' : 'source')
738 738 end
739 739 when 'attachment'
740 740 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
741 741 if attachments && attachment = attachments.detect {|a| a.filename == name }
742 742 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
743 743 :class => 'attachment'
744 744 end
745 745 when 'project'
746 746 if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
747 747 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
748 748 end
749 749 end
750 750 end
751 751 end
752 752 (leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")).html_safe
753 753 end
754 754 end
755 755
756 756 HEADING_RE = /(<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>)/i unless const_defined?(:HEADING_RE)
757 757
758 758 def parse_sections(text, project, obj, attr, only_path, options)
759 759 return unless options[:edit_section_links]
760 760 text.gsub!(HEADING_RE) do
761 761 @current_section += 1
762 762 if @current_section > 1
763 763 content_tag('div',
764 764 link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => @current_section)),
765 765 :class => 'contextual',
766 766 :title => l(:button_edit_section)) + $1
767 767 else
768 768 $1
769 769 end
770 770 end
771 771 end
772 772
773 773 # Headings and TOC
774 774 # Adds ids and links to headings unless options[:headings] is set to false
775 775 def parse_headings(text, project, obj, attr, only_path, options)
776 776 return if options[:headings] == false
777 777
778 778 text.gsub!(HEADING_RE) do
779 779 level, attrs, content = $2.to_i, $3, $4
780 780 item = strip_tags(content).strip
781 781 anchor = sanitize_anchor_name(item)
782 782 # used for single-file wiki export
783 783 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
784 784 @parsed_headings << [level, anchor, item]
785 785 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
786 786 end
787 787 end
788 788
789 789 MACROS_RE = /
790 790 (!)? # escaping
791 791 (
792 792 \{\{ # opening tag
793 793 ([\w]+) # macro name
794 794 (\(([^\}]*)\))? # optional arguments
795 795 \}\} # closing tag
796 796 )
797 797 /x unless const_defined?(:MACROS_RE)
798 798
799 799 # Macros substitution
800 800 def parse_macros(text, project, obj, attr, only_path, options)
801 801 text.gsub!(MACROS_RE) do
802 802 esc, all, macro = $1, $2, $3.downcase
803 803 args = ($5 || '').split(',').each(&:strip)
804 804 if esc.nil?
805 805 begin
806 806 exec_macro(macro, obj, args)
807 807 rescue => e
808 808 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
809 809 end || all
810 810 else
811 811 all
812 812 end
813 813 end
814 814 end
815 815
816 816 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
817 817
818 818 # Renders the TOC with given headings
819 819 def replace_toc(text, headings)
820 820 text.gsub!(TOC_RE) do
821 821 if headings.empty?
822 822 ''
823 823 else
824 824 div_class = 'toc'
825 825 div_class << ' right' if $1 == '>'
826 826 div_class << ' left' if $1 == '<'
827 827 out = "<ul class=\"#{div_class}\"><li>"
828 828 root = headings.map(&:first).min
829 829 current = root
830 830 started = false
831 831 headings.each do |level, anchor, item|
832 832 if level > current
833 833 out << '<ul><li>' * (level - current)
834 834 elsif level < current
835 835 out << "</li></ul>\n" * (current - level) + "</li><li>"
836 836 elsif started
837 837 out << '</li><li>'
838 838 end
839 839 out << "<a href=\"##{anchor}\">#{item}</a>"
840 840 current = level
841 841 started = true
842 842 end
843 843 out << '</li></ul>' * (current - root)
844 844 out << '</li></ul>'
845 845 end
846 846 end
847 847 end
848 848
849 849 # Same as Rails' simple_format helper without using paragraphs
850 850 def simple_format_without_paragraph(text)
851 851 text.to_s.
852 852 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
853 853 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
854 854 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
855 855 html_safe
856 856 end
857 857
858 858 def lang_options_for_select(blank=true)
859 859 (blank ? [["(auto)", ""]] : []) +
860 860 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
861 861 end
862 862
863 863 def label_tag_for(name, option_tags = nil, options = {})
864 864 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
865 865 content_tag("label", label_text)
866 866 end
867 867
868 868 def labelled_tabular_form_for(*args, &proc)
869 869 ActiveSupport::Deprecation.warn "ApplicationHelper#labelled_tabular_form_for is deprecated and will be removed in Redmine 1.5. Use #labelled_form_for instead."
870 870 args << {} unless args.last.is_a?(Hash)
871 871 options = args.last
872 872 options[:html] ||= {}
873 873 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
874 874 options.merge!({:builder => TabularFormBuilder})
875 875 form_for(*args, &proc)
876 876 end
877 877
878 878 def labelled_form_for(*args, &proc)
879 879 args << {} unless args.last.is_a?(Hash)
880 880 options = args.last
881 881 options.merge!({:builder => TabularFormBuilder})
882 882 form_for(*args, &proc)
883 883 end
884 884
885 def labelled_fields_for(*args, &proc)
886 args << {} unless args.last.is_a?(Hash)
887 options = args.last
888 options.merge!({:builder => TabularFormBuilder})
889 fields_for(*args, &proc)
890 end
891
892 def labelled_remote_form_for(*args, &proc)
893 args << {} unless args.last.is_a?(Hash)
894 options = args.last
895 options.merge!({:builder => TabularFormBuilder})
896 remote_form_for(*args, &proc)
897 end
898
885 899 def back_url_hidden_field_tag
886 900 back_url = params[:back_url] || request.env['HTTP_REFERER']
887 901 back_url = CGI.unescape(back_url.to_s)
888 902 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
889 903 end
890 904
891 905 def check_all_links(form_name)
892 906 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
893 907 " | ".html_safe +
894 908 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
895 909 end
896 910
897 911 def progress_bar(pcts, options={})
898 912 pcts = [pcts, pcts] unless pcts.is_a?(Array)
899 913 pcts = pcts.collect(&:round)
900 914 pcts[1] = pcts[1] - pcts[0]
901 915 pcts << (100 - pcts[1] - pcts[0])
902 916 width = options[:width] || '100px;'
903 917 legend = options[:legend] || ''
904 918 content_tag('table',
905 919 content_tag('tr',
906 920 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
907 921 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
908 922 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
909 923 ), :class => 'progress', :style => "width: #{width};").html_safe +
910 924 content_tag('p', legend, :class => 'pourcent').html_safe
911 925 end
912 926
913 927 def checked_image(checked=true)
914 928 if checked
915 929 image_tag 'toggle_check.png'
916 930 end
917 931 end
918 932
919 933 def context_menu(url)
920 934 unless @context_menu_included
921 935 content_for :header_tags do
922 936 javascript_include_tag('context_menu') +
923 937 stylesheet_link_tag('context_menu')
924 938 end
925 939 if l(:direction) == 'rtl'
926 940 content_for :header_tags do
927 941 stylesheet_link_tag('context_menu_rtl')
928 942 end
929 943 end
930 944 @context_menu_included = true
931 945 end
932 946 javascript_tag "new ContextMenu('#{ url_for(url) }')"
933 947 end
934 948
935 949 def context_menu_link(name, url, options={})
936 950 options[:class] ||= ''
937 951 if options.delete(:selected)
938 952 options[:class] << ' icon-checked disabled'
939 953 options[:disabled] = true
940 954 end
941 955 if options.delete(:disabled)
942 956 options.delete(:method)
943 957 options.delete(:confirm)
944 958 options.delete(:onclick)
945 959 options[:class] << ' disabled'
946 960 url = '#'
947 961 end
948 962 link_to h(name), url, options
949 963 end
950 964
951 965 def calendar_for(field_id)
952 966 include_calendar_headers_tags
953 967 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
954 968 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
955 969 end
956 970
957 971 def include_calendar_headers_tags
958 972 unless @calendar_headers_tags_included
959 973 @calendar_headers_tags_included = true
960 974 content_for :header_tags do
961 975 start_of_week = case Setting.start_of_week.to_i
962 976 when 1
963 977 'Calendar._FD = 1;' # Monday
964 978 when 7
965 979 'Calendar._FD = 0;' # Sunday
966 980 when 6
967 981 'Calendar._FD = 6;' # Saturday
968 982 else
969 983 '' # use language
970 984 end
971 985
972 986 javascript_include_tag('calendar/calendar') +
973 987 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
974 988 javascript_tag(start_of_week) +
975 989 javascript_include_tag('calendar/calendar-setup') +
976 990 stylesheet_link_tag('calendar')
977 991 end
978 992 end
979 993 end
980 994
981 995 def content_for(name, content = nil, &block)
982 996 @has_content ||= {}
983 997 @has_content[name] = true
984 998 super(name, content, &block)
985 999 end
986 1000
987 1001 def has_content?(name)
988 1002 (@has_content && @has_content[name]) || false
989 1003 end
990 1004
991 1005 def email_delivery_enabled?
992 1006 !!ActionMailer::Base.perform_deliveries
993 1007 end
994 1008
995 1009 # Returns the avatar image tag for the given +user+ if avatars are enabled
996 1010 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
997 1011 def avatar(user, options = { })
998 1012 if Setting.gravatar_enabled?
999 1013 options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
1000 1014 email = nil
1001 1015 if user.respond_to?(:mail)
1002 1016 email = user.mail
1003 1017 elsif user.to_s =~ %r{<(.+?)>}
1004 1018 email = $1
1005 1019 end
1006 1020 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
1007 1021 else
1008 1022 ''
1009 1023 end
1010 1024 end
1011 1025
1012 1026 def sanitize_anchor_name(anchor)
1013 1027 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1014 1028 end
1015 1029
1016 1030 # Returns the javascript tags that are included in the html layout head
1017 1031 def javascript_heads
1018 1032 tags = javascript_include_tag(:defaults)
1019 1033 unless User.current.pref.warn_on_leaving_unsaved == '0'
1020 1034 tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
1021 1035 end
1022 1036 tags
1023 1037 end
1024 1038
1025 1039 def favicon
1026 1040 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
1027 1041 end
1028 1042
1029 1043 def robot_exclusion_tag
1030 1044 '<meta name="robots" content="noindex,follow,noarchive" />'.html_safe
1031 1045 end
1032 1046
1033 1047 # Returns true if arg is expected in the API response
1034 1048 def include_in_api_response?(arg)
1035 1049 unless @included_in_api_response
1036 1050 param = params[:include]
1037 1051 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
1038 1052 @included_in_api_response.collect!(&:strip)
1039 1053 end
1040 1054 @included_in_api_response.include?(arg.to_s)
1041 1055 end
1042 1056
1043 1057 # Returns options or nil if nometa param or X-Redmine-Nometa header
1044 1058 # was set in the request
1045 1059 def api_meta(options)
1046 1060 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
1047 1061 # compatibility mode for activeresource clients that raise
1048 1062 # an error when unserializing an array with attributes
1049 1063 nil
1050 1064 else
1051 1065 options
1052 1066 end
1053 1067 end
1054 1068
1055 1069 private
1056 1070
1057 1071 def wiki_helper
1058 1072 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
1059 1073 extend helper
1060 1074 return self
1061 1075 end
1062 1076
1063 1077 def link_to_content_update(text, url_params = {}, html_options = {})
1064 1078 link_to(text, url_params, html_options)
1065 1079 end
1066 1080 end
@@ -1,6 +1,6
1 1 <h2><%= link_to l(:label_issue_status_plural), issue_statuses_path %> &#187; <%=h @issue_status %></h2>
2 2
3 <% form_for @issue_status, :builder => TabularFormBuilder do |f| %>
3 <% labelled_form_for @issue_status do |f| %>
4 4 <%= render :partial => 'form', :locals => {:f => f} %>
5 5 <%= submit_tag l(:button_save) %>
6 6 <% end %>
@@ -1,6 +1,6
1 1 <h2><%= link_to l(:label_issue_status_plural), issue_statuses_path %> &#187; <%=l(:label_issue_status_new)%></h2>
2 2
3 <% form_for @issue_status, :builder => TabularFormBuilder do |f| %>
3 <% labelled_form_for @issue_status do |f| %>
4 4 <%= render :partial => 'form', :locals => {:f => f} %>
5 5 <%= submit_tag l(:button_create) %>
6 6 <% end %>
@@ -1,50 +1,50
1 <% fields_for :issue, @issue, :builder => TabularFormBuilder do |f| %>
1 <% labelled_fields_for :issue, @issue do |f| %>
2 2
3 3 <div class="splitcontentleft">
4 4 <% if @issue.new_record? || @allowed_statuses.any? %>
5 5 <p><%= f.select :status_id, (@allowed_statuses.collect {|p| [p.name, p.id]}), :required => true %></p>
6 6 <% else %>
7 7 <p><label><%= l(:field_status) %></label> <%= h(@issue.status.name) %></p>
8 8 <% end %>
9 9
10 10 <p><%= f.select :priority_id, (@priorities.collect {|p| [p.name, p.id]}), {:required => true}, :disabled => !@issue.leaf? %></p>
11 11 <p><%= f.select :assigned_to_id, principals_options_for_select(@issue.assignable_users, @issue.assigned_to), :include_blank => true %></p>
12 12 <% unless @project.issue_categories.empty? %>
13 13 <p><%= f.select :category_id, (@project.issue_categories.collect {|c| [c.name, c.id]}), :include_blank => true %>
14 14 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
15 15 l(:label_issue_category_new),
16 16 'issue_category[name]',
17 17 {:controller => 'issue_categories', :action => 'create', :project_id => @project},
18 18 :title => l(:label_issue_category_new),
19 19 :tabindex => 199) if authorize_for('issue_categories', 'new') %></p>
20 20 <% end %>
21 21 <% unless @issue.assignable_versions.empty? %>
22 22 <p><%= f.select :fixed_version_id, version_options_for_select(@issue.assignable_versions, @issue.fixed_version), :include_blank => true %>
23 23 <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'),
24 24 l(:label_version_new),
25 25 'version[name]',
26 26 {:controller => 'versions', :action => 'create', :project_id => @project},
27 27 :title => l(:label_version_new),
28 28 :tabindex => 200) if authorize_for('versions', 'new') %>
29 29 </p>
30 30 <% end %>
31 31 </div>
32 32
33 33 <div class="splitcontentright">
34 34 <% if User.current.allowed_to?(:manage_subtasks, @project) %>
35 35 <p id="parent_issue"><%= f.text_field :parent_issue_id, :size => 10 %></p>
36 36 <div id="parent_issue_candidates" class="autocomplete"></div>
37 37 <%= javascript_tag "observeParentIssueField('#{auto_complete_issues_path(:id => @issue, :project_id => @project) }')" %>
38 38 <% end %>
39 39 <p><%= f.text_field :start_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_start_date') if @issue.leaf? %></p>
40 40 <p><%= f.text_field :due_date, :size => 10, :disabled => !@issue.leaf? %><%= calendar_for('issue_due_date') if @issue.leaf? %></p>
41 41 <p><%= f.text_field :estimated_hours, :size => 3, :disabled => !@issue.leaf? %> <%= l(:field_hours) %></p>
42 42 <% if @issue.leaf? && Issue.use_field_for_done_ratio? %>
43 43 <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
44 44 <% end %>
45 45 </div>
46 46
47 47 <div style="clear:both;"> </div>
48 48 <%= render :partial => 'issues/form_custom_fields' %>
49 49
50 50 <% end %>
@@ -1,46 +1,46
1 1 <% labelled_form_for @issue, :html => {:id => 'issue-form', :multipart => true} do |f| %>
2 2 <%= error_messages_for 'issue', 'time_entry' %>
3 3 <div class="box">
4 4 <% if @edit_allowed || !@allowed_statuses.empty? %>
5 5 <fieldset class="tabular"><legend><%= l(:label_change_properties) %></legend>
6 6 <%= render :partial => (@edit_allowed ? 'form' : 'form_update'), :locals => {:f => f} %>
7 7 </fieldset>
8 8 <% end %>
9 9 <% if User.current.allowed_to?(:log_time, @project) %>
10 10 <fieldset class="tabular"><legend><%= l(:button_log_time) %></legend>
11 <% fields_for :time_entry, @time_entry, { :builder => TabularFormBuilder, :lang => current_language} do |time_entry| %>
11 <% labelled_fields_for :time_entry, @time_entry do |time_entry| %>
12 12 <div class="splitcontentleft">
13 13 <p><%= time_entry.text_field :hours, :size => 6, :label => :label_spent_time %> <%= l(:field_hours) %></p>
14 14 </div>
15 15 <div class="splitcontentright">
16 16 <p><%= time_entry.select :activity_id, activity_collection_for_select_options %></p>
17 17 </div>
18 18 <p><%= time_entry.text_field :comments, :size => 60 %></p>
19 19 <% @time_entry.custom_field_values.each do |value| %>
20 20 <p><%= custom_field_tag_with_label :time_entry, value %></p>
21 21 <% end %>
22 22 <% end %>
23 23 </fieldset>
24 24 <% end %>
25 25
26 26 <fieldset><legend><%= l(:field_notes) %></legend>
27 27 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
28 28 <%= wikitoolbar_for 'notes' %>
29 29 <%= call_hook(:view_issues_edit_notes_bottom, { :issue => @issue, :notes => @notes, :form => f }) %>
30 30
31 31 <p><%=l(:label_attachment_plural)%><br /><%= render :partial => 'attachments/form' %></p>
32 32 </fieldset>
33 33 </div>
34 34
35 35 <%= f.hidden_field :lock_version %>
36 36 <%= submit_tag l(:button_submit) %>
37 37 <%= link_to_remote l(:label_preview),
38 38 { :url => preview_issue_path(:project_id => @project, :id => @issue),
39 39 :method => 'post',
40 40 :update => 'preview',
41 41 :with => 'Form.serialize("issue-form")',
42 42 :complete => "Element.scrollTo('preview')"
43 43 }, :accesskey => accesskey(:preview) %>
44 44 <% end %>
45 45
46 46 <div id="preview" class="wiki"></div>
@@ -1,51 +1,49
1 1 <div class="contextual">
2 2 <%= link_to(l(:button_change_password), {:action => 'password'}, :class => 'icon icon-passwd') if @user.change_password_allowed? %>
3 3 <%= call_hook(:view_my_account_contextual, :user => @user)%>
4 4 </div>
5 5
6 6 <h2><%=l(:label_my_account)%></h2>
7 7 <%= error_messages_for 'user' %>
8 8
9 <% form_for :user, @user, :url => { :action => "account" },
10 :builder => TabularFormBuilder,
11 :lang => current_language,
9 <% labelled_form_for :user, @user, :url => { :action => "account" },
12 10 :html => { :id => 'my_account_form' } do |f| %>
13 11 <div class="splitcontentleft">
14 12 <fieldset class="box tabular">
15 13 <legend><%=l(:label_information_plural)%></legend>
16 14 <p><%= f.text_field :firstname, :required => true %></p>
17 15 <p><%= f.text_field :lastname, :required => true %></p>
18 16 <p><%= f.text_field :mail, :required => true %></p>
19 17 <p><%= f.select :language, lang_options_for_select %></p>
20 18 <% if Setting.openid? %>
21 19 <p><%= f.text_field :identity_url %></p>
22 20 <% end %>
23 21
24 22 <% @user.custom_field_values.select(&:editable?).each do |value| %>
25 23 <p><%= custom_field_tag_with_label :user, value %></p>
26 24 <% end %>
27 25 <%= call_hook(:view_my_account, :user => @user, :form => f) %>
28 26 </fieldset>
29 27
30 28 <%= submit_tag l(:button_save) %>
31 29 </div>
32 30
33 31 <div class="splitcontentright">
34 32 <fieldset class="box">
35 33 <legend><%=l(:field_mail_notification)%></legend>
36 34 <%= render :partial => 'users/mail_notifications' %>
37 35 </fieldset>
38 36
39 37 <fieldset class="box tabular">
40 38 <legend><%=l(:label_preferences)%></legend>
41 39 <%= render :partial => 'users/preferences' %>
42 40 </fieldset>
43 41
44 42 </div>
45 43 <% end %>
46 44
47 45 <% content_for :sidebar do %>
48 46 <%= render :partial => 'sidebar' %>
49 47 <% end %>
50 48
51 49 <% html_title(l(:label_my_account)) -%>
@@ -1,41 +1,39
1 <% remote_form_for :repository, @repository,
2 :url => { :controller => 'repositories', :action => 'edit', :id => @project },
3 :builder => TabularFormBuilder,
4 :lang => current_language do |f| %>
1 <% labelled_remote_form_for :repository, @repository,
2 :url => { :controller => 'repositories', :action => 'edit', :id => @project } do |f| %>
5 3
6 4 <%= error_messages_for 'repository' %>
7 5
8 6 <div class="box tabular">
9 7 <p>
10 8 <%= label_tag('repository_scm', l(:label_scm)) %><%= scm_select_tag(@repository) %>
11 9 <% if @repository && ! @repository.class.scm_available %>
12 10 <br />
13 11 <em><%= content_tag 'span', l(:text_scm_command_not_available), :class => 'error' %></em>
14 12 <% end %>
15 13 </p>
16 14 <% button_disabled = true %>
17 15 <% if @repository %>
18 16 <% button_disabled = ! @repository.class.scm_available %>
19 17 <%= repository_field_tags(f, @repository)%>
20 18 <% end %>
21 19 </div>
22 20
23 21 <div class="contextual">
24 22 <% if @repository && !@repository.new_record? %>
25 23 <%= link_to(l(:label_user_plural),
26 24 {
27 25 :controller => 'repositories',
28 26 :action => 'committers',
29 27 :id => @project
30 28 },
31 29 :class => 'icon icon-user') %>
32 30 <%= link_to(l(:button_delete), {:controller => 'repositories', :action => 'destroy', :id => @project},
33 31 :confirm => l(:text_are_you_sure),
34 32 :method => :post,
35 33 :class => 'icon icon-del') %>
36 34 <% end %>
37 35 </div>
38 36
39 37 <%= submit_tag((@repository.nil? || @repository.new_record?) ? l(:button_create) : l(:button_save),
40 38 :disabled => button_disabled) %>
41 39 <% end %>
@@ -1,19 +1,17
1 <% remote_form_for :wiki, @wiki,
2 :url => { :controller => 'wikis', :action => 'edit', :id => @project },
3 :builder => TabularFormBuilder,
4 :lang => current_language do |f| %>
1 <% labelled_remote_form_for :wiki, @wiki,
2 :url => { :controller => 'wikis', :action => 'edit', :id => @project } do |f| %>
5 3
6 4 <%= error_messages_for 'wiki' %>
7 5
8 6 <div class="box tabular">
9 7 <p><%= f.text_field :start_page, :size => 60, :required => true %><br />
10 8 <em><%= l(:text_unallowed_characters) %>: , . / ? ; : |</em></p>
11 9 </div>
12 10
13 11 <div class="contextual">
14 12 <%= link_to(l(:button_delete), {:controller => 'wikis', :action => 'destroy', :id => @project},
15 13 :class => 'icon icon-del') if @wiki && !@wiki.new_record? %>
16 14 </div>
17 15
18 16 <%= submit_tag((@wiki.nil? || @wiki.new_record?) ? l(:button_create) : l(:button_save)) %>
19 17 <% end %>
@@ -1,5 +1,5
1 1 <h2><%= link_to l(:label_tracker_plural), trackers_path %> &#187; <%=h @tracker %></h2>
2 2
3 <% form_for @tracker, :builder => TabularFormBuilder do |f| %>
3 <% labelled_form_for @tracker do |f| %>
4 4 <%= render :partial => 'form', :locals => { :f => f } %>
5 5 <% end %>
@@ -1,5 +1,5
1 1 <h2><%= link_to l(:label_tracker_plural), trackers_path %> &#187; <%=l(:label_tracker_new)%></h2>
2 2
3 <% form_for @tracker, :builder => TabularFormBuilder do |f| %>
3 <% labelled_form_for @tracker do |f| %>
4 4 <%= render :partial => 'form', :locals => { :f => f } %>
5 5 <% end %>
@@ -1,7 +1,7
1 <% form_for @user, :builder => TabularFormBuilder do |f| %>
1 <% labelled_form_for @user do |f| %>
2 2 <%= render :partial => 'form', :locals => { :f => f } %>
3 3 <% if @user.active? && email_delivery_enabled? -%>
4 4 <p><label><%= check_box_tag 'send_information', 1, true %> <%= l(:label_send_information) %></label></p>
5 5 <% end -%>
6 6 <p><%= submit_tag l(:button_save) %></p>
7 7 <% end %>
@@ -1,7 +1,7
1 <% fields_for :pref, @user.pref, :builder => TabularFormBuilder, :lang => current_language do |pref_fields| %>
1 <% labelled_fields_for :pref, @user.pref do |pref_fields| %>
2 2 <p><%= pref_fields.check_box :hide_mail %></p>
3 3 <p><%= pref_fields.select :time_zone, ActiveSupport::TimeZone.all.collect {|z| [ z.to_s, z.name ]}, :include_blank => true %></p>
4 4 <p><%= pref_fields.select :comments_sorting, [[l(:label_chronological_order), 'asc'], [l(:label_reverse_chronological_order), 'desc']] %></p>
5 5 <p><%= pref_fields.check_box :warn_on_leaving_unsaved %></p>
6 6 <% end %>
7 7
@@ -1,12 +1,12
1 1 <h2><%= link_to l(:label_user_plural), users_path %> &#187; <%=l(:label_user_new)%></h2>
2 2
3 <% form_for @user, :builder => TabularFormBuilder do |f| %>
3 <% labelled_form_for @user do |f| %>
4 4 <%= render :partial => 'form', :locals => { :f => f } %>
5 5 <% if email_delivery_enabled? %>
6 6 <p><label><%= check_box_tag 'send_information', 1, true %> <%= l(:label_send_information) %></label></p>
7 7 <% end %>
8 8 <p>
9 9 <%= submit_tag l(:button_create) %>
10 10 <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
11 11 </p>
12 12 <% end %>
General Comments 0
You need to be logged in to leave comments. Login now