comparison app/helpers/application_helper.rb @ 1298:4f746d8966dd redmine_2.3_integration

Merge from redmine-2.3 branch to create new branch redmine-2.3-integration
author Chris Cannam
date Fri, 14 Jun 2013 09:28:30 +0100
parents 0a574315af3e 622f24f53b42
children
comparison
equal deleted inserted replaced
1297:0a574315af3e 1298:4f746d8966dd
1 # encoding: utf-8 1 # encoding: utf-8
2 # 2 #
3 # Redmine - project management software 3 # Redmine - project management software
4 # Copyright (C) 2006-2012 Jean-Philippe Lang 4 # Copyright (C) 2006-2013 Jean-Philippe Lang
5 # 5 #
6 # This program is free software; you can redistribute it and/or 6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License 7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2 8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version. 9 # of the License, or (at your option) any later version.
22 22
23 module ApplicationHelper 23 module ApplicationHelper
24 include Redmine::WikiFormatting::Macros::Definitions 24 include Redmine::WikiFormatting::Macros::Definitions
25 include Redmine::I18n 25 include Redmine::I18n
26 include GravatarHelper::PublicMethods 26 include GravatarHelper::PublicMethods
27 include Redmine::Pagination::Helper
27 28
28 extend Forwardable 29 extend Forwardable
29 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter 30 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
30 31
31 # Return true if user is authorized for controller/action, otherwise false 32 # Return true if user is authorized for controller/action, otherwise false
88 # Options: 89 # Options:
89 # * :text - Link text (default to attachment filename) 90 # * :text - Link text (default to attachment filename)
90 # * :download - Force download (default: false) 91 # * :download - Force download (default: false)
91 def link_to_attachment(attachment, options={}) 92 def link_to_attachment(attachment, options={})
92 text = options.delete(:text) || attachment.filename 93 text = options.delete(:text) || attachment.filename
93 action = options.delete(:download) ? 'download' : 'show' 94 route_method = options.delete(:download) ? :download_named_attachment_path : :named_attachment_path
94 opt_only_path = {} 95 html_options = options.slice!(:only_path)
95 opt_only_path[:only_path] = (options[:only_path] == false ? false : true) 96 url = send(route_method, attachment, attachment.filename, options)
96 options.delete(:only_path) 97 link_to text, url, html_options
97 link_to(h(text),
98 {:controller => 'attachments', :action => action,
99 :id => attachment, :filename => attachment.filename}.merge(opt_only_path),
100 options)
101 end 98 end
102 99
103 # Generates a link to a SCM revision 100 # Generates a link to a SCM revision
104 # Options: 101 # Options:
105 # * :text - Link text (default to the formatted revision) 102 # * :text - Link text (default to the formatted revision)
117 end 114 end
118 115
119 # Generates a link to a message 116 # Generates a link to a message
120 def link_to_message(message, options={}, html_options = nil) 117 def link_to_message(message, options={}, html_options = nil)
121 link_to( 118 link_to(
122 h(truncate(message.subject, :length => 60)), 119 truncate(message.subject, :length => 60),
123 { :controller => 'messages', :action => 'show', 120 board_message_path(message.board_id, message.parent_id || message.id, {
124 :board_id => message.board_id,
125 :id => (message.parent_id || message.id),
126 :r => (message.parent_id && message.id), 121 :r => (message.parent_id && message.id),
127 :anchor => (message.parent_id ? "message-#{message.id}" : nil) 122 :anchor => (message.parent_id ? "message-#{message.id}" : nil)
128 }.merge(options), 123 }.merge(options)),
129 html_options 124 html_options
130 ) 125 )
131 end 126 end
132 127
133 # Generates a link to a project if active 128 # Generates a link to a project if active
134 # Examples: 129 # Examples:
135 # 130 #
136 # link_to_project(project) # => link to the specified project overview 131 # link_to_project(project) # => link to the specified project overview
137 # link_to_project(project, :action=>'settings') # => link to project settings
138 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options 132 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
139 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview) 133 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
140 # 134 #
141 def link_to_project(project, options={}, html_options = nil) 135 def link_to_project(project, options={}, html_options = nil)
142 if project.archived? 136 if project.archived?
143 h(project) 137 h(project.name)
144 else 138 elsif options.key?(:action)
139 ActiveSupport::Deprecation.warn "#link_to_project with :action option is deprecated and will be removed in Redmine 3.0."
145 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options) 140 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
146 link_to(h(project), url, html_options) 141 link_to project.name, url, html_options
142 else
143 link_to project.name, project_path(project, options), html_options
144 end
145 end
146
147 # Generates a link to a project settings if active
148 def link_to_project_settings(project, options={}, html_options=nil)
149 if project.active?
150 link_to project.name, settings_project_path(project, options), html_options
151 elsif project.archived?
152 h(project.name)
153 else
154 link_to project.name, project_path(project, options), html_options
147 end 155 end
148 end 156 end
149 157
150 def wiki_page_path(page, options={}) 158 def wiki_page_path(page, options={})
151 url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options)) 159 url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options))
152 end 160 end
153 161
154 def thumbnail_tag(attachment) 162 def thumbnail_tag(attachment)
155 link_to image_tag(url_for(:controller => 'attachments', :action => 'thumbnail', :id => attachment)), 163 link_to image_tag(thumbnail_path(attachment)),
156 {:controller => 'attachments', :action => 'show', :id => attachment, :filename => attachment.filename}, 164 named_attachment_path(attachment, attachment.filename),
157 :title => attachment.filename 165 :title => attachment.filename
158 end 166 end
159 167
160 def toggle_link(name, id, options={}) 168 def toggle_link(name, id, options={})
161 onclick = "$('##{id}').toggle(); " 169 onclick = "$('##{id}').toggle(); "
185 ).gsub(/[\r\n]+/, "<br />").html_safe 193 ).gsub(/[\r\n]+/, "<br />").html_safe
186 end 194 end
187 195
188 def format_version_name(version) 196 def format_version_name(version)
189 if version.project == @project 197 if version.project == @project
190 h(version) 198 h(version)
191 else 199 else
192 h("#{version.project} - #{version}") 200 h("#{version.project} - #{version}")
193 end 201 end
194 end 202 end
195 203
339 347
340 # Options for the new membership projects combo-box 348 # Options for the new membership projects combo-box
341 def options_for_membership_project_select(principal, projects) 349 def options_for_membership_project_select(principal, projects)
342 options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---") 350 options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
343 options << project_tree_options_for_select(projects) do |p| 351 options << project_tree_options_for_select(projects) do |p|
344 {:disabled => principal.projects.include?(p)} 352 {:disabled => principal.projects.to_a.include?(p)}
345 end 353 end
346 options 354 options
347 end 355 end
348 356
349 # Truncates and returns the string as a single line 357 # Truncates and returns the string as a single line
393 end 401 end
394 402
395 def to_path_param(path) 403 def to_path_param(path)
396 str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/") 404 str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/")
397 str.blank? ? nil : str 405 str.blank? ? nil : str
398 end
399
400 def pagination_links_full(paginator, count=nil, options={})
401 page_param = options.delete(:page_param) || :page
402 per_page_links = options.delete(:per_page_links)
403 url_param = params.dup
404
405 html = ''
406 if paginator.current.previous
407 # \xc2\xab(utf-8) = &#171;
408 html << link_to_content_update(
409 "\xc2\xab " + l(:label_previous),
410 url_param.merge(page_param => paginator.current.previous)) + ' '
411 end
412
413 html << (pagination_links_each(paginator, options) do |n|
414 link_to_content_update(n.to_s, url_param.merge(page_param => n))
415 end || '')
416
417 if paginator.current.next
418 # \xc2\xbb(utf-8) = &#187;
419 html << ' ' + link_to_content_update(
420 (l(:label_next) + " \xc2\xbb"),
421 url_param.merge(page_param => paginator.current.next))
422 end
423
424 unless count.nil?
425 html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
426 if per_page_links != false && links = per_page_links(paginator.items_per_page, count)
427 html << " | #{links}"
428 end
429 end
430
431 html.html_safe
432 end
433
434 def per_page_links(selected=nil, item_count=nil)
435 values = Setting.per_page_options_array
436 if item_count && values.any?
437 if item_count > values.first
438 max = values.detect {|value| value >= item_count} || item_count
439 else
440 max = item_count
441 end
442 values = values.select {|value| value <= max || value == selected}
443 end
444 if values.empty? || (values.size == 1 && values.first == selected)
445 return nil
446 end
447 links = values.collect do |n|
448 n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
449 end
450 l(:label_display_per_page, links.join(', '))
451 end 406 end
452 407
453 def reorder_links(name, url, method = :post) 408 def reorder_links(name, url, method = :post)
454 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), 409 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
455 url.merge({"#{name}[move_to]" => 'highest'}), 410 url.merge({"#{name}[move_to]" => 'highest'}),
527 css << 'action-' + action_name 482 css << 'action-' + action_name
528 css.join(' ') 483 css.join(' ')
529 end 484 end
530 485
531 def accesskey(s) 486 def accesskey(s)
532 Redmine::AccessKeys.key_for s 487 @used_accesskeys ||= []
488 key = Redmine::AccessKeys.key_for(s)
489 return nil if @used_accesskeys.include?(key)
490 @used_accesskeys << key
491 key
533 end 492 end
534 493
535 # Formats text according to system settings. 494 # Formats text according to system settings.
536 # 2 ways to call this method: 495 # 2 ways to call this method:
537 # * with a String: textilizable(text, options) 496 # * with a String: textilizable(text, options)
615 if attachments.present? 574 if attachments.present?
616 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m| 575 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpe|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
617 filename, ext, alt, alttext = $1.downcase, $2, $3, $4 576 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
618 # search for the picture in attachments 577 # search for the picture in attachments
619 if found = Attachment.latest_attach(attachments, filename) 578 if found = Attachment.latest_attach(attachments, filename)
620 image_url = url_for :only_path => only_path, :controller => 'attachments', 579 image_url = download_named_attachment_path(found, found.filename, :only_path => only_path)
621 :action => 'download', :id => found
622 desc = found.description.to_s.gsub('"', '') 580 desc = found.description.to_s.gsub('"', '')
623 if !desc.blank? && alttext.blank? 581 if !desc.blank? && alttext.blank?
624 alt = " title=\"#{desc}\" alt=\"#{desc}\"" 582 alt = " title=\"#{desc}\" alt=\"#{desc}\""
625 end 583 end
626 "src=\"#{image_url}\"#{alt}" 584 "src=\"#{image_url}\"#{alt}"
645 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m| 603 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
646 link_project = project 604 link_project = project
647 esc, all, page, title = $1, $2, $3, $5 605 esc, all, page, title = $1, $2, $3, $5
648 if esc.nil? 606 if esc.nil?
649 if page =~ /^([^\:]+)\:(.*)$/ 607 if page =~ /^([^\:]+)\:(.*)$/
650 link_project = Project.find_by_identifier($1) || Project.find_by_name($1) 608 identifier, page = $1, $2
651 page = $2 609 link_project = Project.find_by_identifier(identifier) || Project.find_by_name(identifier)
652 title ||= $1 if page.blank? 610 title ||= identifier if page.blank?
653 end 611 end
654 612
655 if link_project && link_project.wiki 613 if link_project && link_project.wiki
656 # extract anchor 614 # extract anchor
657 anchor = nil 615 anchor = nil
812 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier} 770 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
813 else 771 else
814 repository = project.repository 772 repository = project.repository
815 end 773 end
816 if prefix == 'commit' 774 if prefix == 'commit'
817 if repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%"])) 775 if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
818 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}, 776 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},
819 :class => 'changeset', 777 :class => 'changeset',
820 :title => truncate_single_line(h(changeset.comments), :length => 100) 778 :title => truncate_single_line(changeset.comments, :length => 100)
821 end 779 end
822 else 780 else
823 if repository && User.current.allowed_to?(:browse_repository, project) 781 if repository && User.current.allowed_to?(:browse_repository, project)
824 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$} 782 name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
825 path, rev, anchor = $1, $3, $5 783 path, rev, anchor = $1, $3, $5
826 link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param, 784 link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
827 :path => to_path_param(path), 785 :path => to_path_param(path),
828 :rev => rev, 786 :rev => rev,
829 :anchor => anchor}, 787 :anchor => anchor},
833 repo_prefix = nil 791 repo_prefix = nil
834 end 792 end
835 when 'attachment' 793 when 'attachment'
836 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil) 794 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
837 if attachments && attachment = Attachment.latest_attach(attachments, name) 795 if attachments && attachment = Attachment.latest_attach(attachments, name)
838 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment}, 796 link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment')
839 :class => 'attachment'
840 end 797 end
841 when 'project' 798 when 'project'
842 if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}]) 799 if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
843 link = link_to_project(p, {:only_path => only_path}, :class => 'project') 800 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
844 end 801 end
845 end 802 end
846 end 803 end
847 end 804 end
1090 content_tag('tr', 1047 content_tag('tr',
1091 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) + 1048 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
1092 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) + 1049 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
1093 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe) 1050 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
1094 ), :class => 'progress', :style => "width: #{width};").html_safe + 1051 ), :class => 'progress', :style => "width: #{width};").html_safe +
1095 content_tag('p', legend, :class => 'pourcent').html_safe 1052 content_tag('p', legend, :class => 'percent').html_safe
1096 end 1053 end
1097 1054
1098 def checked_image(checked=true) 1055 def checked_image(checked=true)
1099 if checked 1056 if checked
1100 image_tag 'toggle_check.png' 1057 image_tag 'toggle_check.png'
1134 1091
1135 tags = javascript_tag( 1092 tags = javascript_tag(
1136 "var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " + 1093 "var datepickerOptions={dateFormat: 'yy-mm-dd', firstDay: #{start_of_week}, " +
1137 "showOn: 'button', buttonImageOnly: true, buttonImage: '" + 1094 "showOn: 'button', buttonImageOnly: true, buttonImage: '" +
1138 path_to_image('/images/calendar.png') + 1095 path_to_image('/images/calendar.png') +
1139 "', showButtonPanel: true};") 1096 "', showButtonPanel: true, showWeek: true, showOtherMonths: true, selectOtherMonths: true};")
1140 jquery_locale = l('jquery.locale', :default => current_language.to_s) 1097 jquery_locale = l('jquery.locale', :default => current_language.to_s)
1141 unless jquery_locale == 'en' 1098 unless jquery_locale == 'en'
1142 tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js") 1099 tags << javascript_include_tag("i18n/jquery.ui.datepicker-#{jquery_locale}.js")
1143 end 1100 end
1144 tags 1101 tags
1238 end 1195 end
1239 end 1196 end
1240 1197
1241 def sanitize_anchor_name(anchor) 1198 def sanitize_anchor_name(anchor)
1242 if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java' 1199 if ''.respond_to?(:encoding) || RUBY_PLATFORM == 'java'
1243 anchor.gsub(%r{[^\p{Word}\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-') 1200 anchor.gsub(%r{[^\s\-\p{Word}]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1244 else 1201 else
1245 # TODO: remove when ruby1.8 is no longer supported 1202 # TODO: remove when ruby1.8 is no longer supported
1246 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-') 1203 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
1247 end 1204 end
1248 end 1205 end
1249 1206
1250 # Returns the javascript tags that are included in the html layout head 1207 # Returns the javascript tags that are included in the html layout head
1251 def javascript_heads 1208 def javascript_heads
1252 tags = javascript_include_tag('jquery-1.7.2-ui-1.8.21-ujs-2.0.3', 'application') 1209 tags = javascript_include_tag('jquery-1.8.3-ui-1.9.2-ujs-2.0.3', 'application')
1253 unless User.current.pref.warn_on_leaving_unsaved == '0' 1210 unless User.current.pref.warn_on_leaving_unsaved == '0'
1254 tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });") 1211 tags << "\n".html_safe + javascript_tag("$(window).load(function(){ warnLeavingUnsaved('#{escape_javascript l(:text_warn_on_leaving_unsaved)}'); });")
1255 end 1212 end
1256 tags 1213 tags
1257 end 1214 end