diff app/helpers/application_helper.rb @ 1517:dffacf8a6908 redmine-2.5

Update to Redmine SVN revision 13367 on 2.5-stable branch
author Chris Cannam
date Tue, 09 Sep 2014 09:29:00 +0100
parents e248c7af89ec
children a1bdbf8a87d5
line wrap: on
line diff
--- a/app/helpers/application_helper.rb	Tue Sep 09 09:28:31 2014 +0100
+++ b/app/helpers/application_helper.rb	Tue Sep 09 09:29:00 2014 +0100
@@ -72,15 +72,16 @@
     subject = nil
     text = options[:tracker] == false ? "##{issue.id}" : "#{issue.tracker} ##{issue.id}"
     if options[:subject] == false
-      title = truncate(issue.subject, :length => 60)
+      title = issue.subject.truncate(60)
     else
       subject = issue.subject
-      if options[:truncate]
-        subject = truncate(subject, :length => options[:truncate])
+      if truncate_length = options[:truncate]
+        subject = subject.truncate(truncate_length)
       end
     end
     only_path = options[:only_path].nil? ? true : options[:only_path]
-    s = link_to text, issue_path(issue, :only_path => only_path), :class => issue.css_classes, :title => title
+    s = link_to(text, issue_path(issue, :only_path => only_path),
+                :class => issue.css_classes, :title => title)
     s << h(": #{subject}") if subject
     s = h("#{issue.project} - ") + s if options[:project]
     s
@@ -117,7 +118,7 @@
   # Generates a link to a message
   def link_to_message(message, options={}, html_options = nil)
     link_to(
-      truncate(message.subject, :length => 60),
+      message.subject.truncate(60),
       board_message_path(message.board_id, message.parent_id || message.id, {
         :r => (message.parent_id && message.id),
         :anchor => (message.parent_id ? "message-#{message.id}" : nil)
@@ -156,6 +157,47 @@
     end
   end
 
+  # Helper that formats object for html or text rendering
+  def format_object(object, html=true)
+    case object.class.name
+    when 'Array'
+      object.map {|o| format_object(o, html)}.join(', ').html_safe
+    when 'Time'
+      format_time(object)
+    when 'Date'
+      format_date(object)
+    when 'Fixnum'
+      object.to_s
+    when 'Float'
+      sprintf "%.2f", object
+    when 'User'
+      html ? link_to_user(object) : object.to_s
+    when 'Project'
+      html ? link_to_project(object) : object.to_s
+    when 'Version'
+      html ? link_to(object.name, version_path(object)) : object.to_s
+    when 'TrueClass'
+      l(:general_text_Yes)
+    when 'FalseClass'
+      l(:general_text_No)
+    when 'Issue'
+      object.visible? && html ? link_to_issue(object) : "##{object.id}"
+    when 'CustomValue', 'CustomFieldValue'
+      if object.custom_field
+        f = object.custom_field.format.formatted_custom_value(self, object, html)
+        if f.nil? || f.is_a?(String)
+          f
+        else
+          format_object(f, html)
+        end
+      else
+        object.value.to_s
+      end
+    else
+      html ? h(object) : object.to_s
+    end
+  end
+
   def wiki_page_path(page, options={})
     url_for({:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title}.merge(options))
   end
@@ -182,7 +224,7 @@
   end
 
   def format_activity_title(text)
-    h(truncate_single_line(text, :length => 100))
+    h(truncate_single_line_raw(text, 100))
   end
 
   def format_activity_day(date)
@@ -190,7 +232,7 @@
   end
 
   def format_activity_description(text)
-    h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')
+    h(text.to_s.truncate(120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')
        ).gsub(/[\r\n]+/, "<br />").html_safe
   end
 
@@ -267,9 +309,13 @@
   end
 
   # Renders tabs and their content
-  def render_tabs(tabs)
+  def render_tabs(tabs, selected=params[:tab])
     if tabs.any?
-      render :partial => 'common/tabs', :locals => {:tabs => tabs}
+      unless tabs.detect {|tab| tab[:name] == selected}
+        selected = nil
+      end
+      selected ||= tabs.first[:name]
+      render :partial => 'common/tabs', :locals => {:tabs => tabs, :selected_tab => selected}
     else
       content_tag 'p', l(:label_no_data), :class => "nodata"
     end
@@ -355,9 +401,17 @@
 
   # Truncates and returns the string as a single line
   def truncate_single_line(string, *args)
+    ActiveSupport::Deprecation.warn(
+      "ApplicationHelper#truncate_single_line is deprecated and will be removed in Rails 4 poring")
+    # Rails 4 ActionView::Helpers::TextHelper#truncate escapes.
+    # So, result is broken.
     truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
   end
 
+  def truncate_single_line_raw(string, length)
+    string.truncate(length).gsub(%r{[\r\n]+}m, ' ')
+  end
+
   # Truncates at line break after 250 characters or options[:length]
   def truncate_lines(string, options={})
     length = options[:length] || 250
@@ -708,21 +762,30 @@
               repository = project.repository
             end
             # project.changesets.visible raises an SQL error because of a double join on repositories
-            if repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(repository.id, identifier))
-              link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.revision},
-                                        :class => 'changeset',
-                                        :title => truncate_single_line(changeset.comments, :length => 100))
+            if repository &&
+                 (changeset = Changeset.visible.
+                                  find_by_repository_id_and_revision(repository.id, identifier))
+              link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"),
+                             {:only_path => only_path, :controller => 'repositories',
+                              :action => 'revision', :id => project,
+                              :repository_id => repository.identifier_param,
+                              :rev => changeset.revision},
+                             :class => 'changeset',
+                             :title => truncate_single_line_raw(changeset.comments, 100))
             end
           end
         elsif sep == '#'
           oid = identifier.to_i
           case prefix
           when nil
-            if oid.to_s == identifier && issue = Issue.visible.find_by_id(oid, :include => :status)
+            if oid.to_s == identifier &&
+                  issue = Issue.visible.includes(:status).find_by_id(oid)
               anchor = comment_id ? "note-#{comment_id}" : nil
-              link = link_to(h("##{oid}#{comment_suffix}"), {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid, :anchor => anchor},
-                                        :class => issue.css_classes,
-                                        :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
+              link = link_to(h("##{oid}#{comment_suffix}"),
+                             {:only_path => only_path, :controller => 'issues',
+                              :action => 'show', :id => oid, :anchor => anchor},
+                             :class => issue.css_classes,
+                             :title => "#{issue.subject.truncate(100)} (#{issue.status.name})")
             end
           when 'document'
             if document = Document.visible.find_by_id(oid)
@@ -735,7 +798,7 @@
                                               :class => 'version'
             end
           when 'message'
-            if message = Message.visible.find_by_id(oid, :include => :parent)
+            if message = Message.visible.includes(:parent).find_by_id(oid)
               link = link_to_message(message, {:only_path => only_path}, :class => 'message')
             end
           when 'forum'
@@ -756,6 +819,7 @@
         elsif sep == ':'
           # removes the double quotes if any
           name = identifier.gsub(%r{^"(.*)"$}, "\\1")
+          name = CGI.unescapeHTML(name)
           case prefix
           when 'document'
             if project && document = project.documents.visible.find_by_title(name)
@@ -790,13 +854,13 @@
                 if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
                   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},
                                                :class => 'changeset',
-                                               :title => truncate_single_line(changeset.comments, :length => 100)
+                                               :title => truncate_single_line_raw(changeset.comments, 100)
                 end
               else
                 if repository && User.current.allowed_to?(:browse_repository, project)
                   name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
                   path, rev, anchor = $1, $3, $5
-                  link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
+                  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,
                                                           :path => to_path_param(path),
                                                           :rev => rev,
                                                           :anchor => anchor},
@@ -806,7 +870,8 @@
               repo_prefix = nil
             end
           when 'attachment'
-            attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
+            attachments = options[:attachments] || []
+            attachments += obj.attachments if obj.respond_to?(:attachments)
             if attachments && attachment = Attachment.latest_attach(attachments, name)
               link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment')
             end
@@ -914,19 +979,20 @@
     end
   end
 
-  TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
+  TOC_RE = /<p>\{\{((<|&lt;)|(>|&gt;))?toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
 
   # Renders the TOC with given headings
   def replace_toc(text, headings)
     text.gsub!(TOC_RE) do
+      left_align, right_align = $2, $3
       # Keep only the 4 first levels
       headings = headings.select{|level, anchor, item| level <= 4}
       if headings.empty?
         ''
       else
         div_class = 'toc'
-        div_class << ' right' if $1 == '>'
-        div_class << ' left' if $1 == '<'
+        div_class << ' right' if right_align
+        div_class << ' left' if left_align
         out = "<ul class=\"#{div_class}\"><li>"
         root = headings.map(&:first).min
         current = root
@@ -1227,7 +1293,21 @@
   end
 
   def favicon
-    "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
+    "<link rel='shortcut icon' href='#{favicon_path}' />".html_safe
+  end
+
+  # Returns the path to the favicon
+  def favicon_path
+    icon = (current_theme && current_theme.favicon?) ? current_theme.favicon_path : '/favicon.ico'
+    image_path(icon)
+  end
+
+  # Returns the full URL to the favicon
+  def favicon_url
+    # TODO: use #image_url introduced in Rails4
+    path = favicon_path
+    base = url_for(:controller => 'welcome', :action => 'index', :only_path => false)
+    base.sub(%r{/+$},'') + '/' + path.sub(%r{^/+},'')
   end
 
   def robot_exclusion_tag