diff lib/redmine/wiki_formatting.rb @ 1115:433d4f72a19b redmine-2.2

Update to Redmine SVN revision 11137 on 2.2-stable branch
author Chris Cannam
date Mon, 07 Jan 2013 12:01:42 +0000
parents cbb26bc654de
children 622f24f53b42
line wrap: on
line diff
--- a/lib/redmine/wiki_formatting.rb	Wed Jun 27 14:54:18 2012 +0100
+++ b/lib/redmine/wiki_formatting.rb	Mon Jan 07 12:01:42 2013 +0000
@@ -1,5 +1,5 @@
 # Redmine - project management software
-# Copyright (C) 2006-2011  Jean-Philippe Lang
+# Copyright (C) 2006-2012  Jean-Philippe Lang
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -15,6 +15,8 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
+require 'digest/md5'
+
 module Redmine
   module WikiFormatting
     class StaleSectionError < Exception; end
@@ -50,7 +52,7 @@
       end
 
       def to_html(format, text, options = {})
-        text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, options[:object], options[:attribute])
+        text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, text, options[:object], options[:attribute])
           # Text retrieved from the cache store may be frozen
           # We need to dup it so we can do in-place substitutions with gsub!
           cache_store.fetch cache_key do
@@ -67,10 +69,10 @@
         (formatter.instance_methods & ['update_section', :update_section]).any?
       end
 
-      # Returns a cache key for the given text +format+, +object+ and +attribute+ or nil if no caching should be done
-      def cache_key_for(format, object, attribute)
-        if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank?
-          "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}"
+      # Returns a cache key for the given text +format+, +text+, +object+ and +attribute+ or nil if no caching should be done
+      def cache_key_for(format, text, object, attribute)
+        if object && attribute && !object.new_record? && format.present?
+          "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{Digest::MD5.hexdigest text}"
         end
       end
 
@@ -80,19 +82,78 @@
       end
     end
 
+    module LinksHelper
+      AUTO_LINK_RE = %r{
+                      (                          # leading text
+                        <\w+.*?>|                # leading HTML tag, or
+                        [^=<>!:'"/]|             # leading punctuation, or
+                        ^                        # beginning of line
+                      )
+                      (
+                        (?:https?://)|           # protocol spec, or
+                        (?:s?ftps?://)|
+                        (?:www\.)                # www.*
+                      )
+                      (
+                        (\S+?)                   # url
+                        (\/)?                    # slash
+                      )
+                      ((?:&gt;)?|[^[:alnum:]_\=\/;\(\)]*?)               # post
+                      (?=<|\s|$)
+                     }x unless const_defined?(:AUTO_LINK_RE)
+
+      # Destructively remplaces urls into clickable links
+      def auto_link!(text)
+        text.gsub!(AUTO_LINK_RE) do
+          all, leading, proto, url, post = $&, $1, $2, $3, $6
+          if leading =~ /<a\s/i || leading =~ /![<>=]?/
+            # don't replace URL's that are already linked
+            # and URL's prefixed with ! !> !< != (textile images)
+            all
+          else
+            # Idea below : an URL with unbalanced parethesis and
+            # ending by ')' is put into external parenthesis
+            if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
+              url=url[0..-2] # discard closing parenth from url
+              post = ")"+post # add closing parenth to post
+            end
+            content = proto + url
+            href = "#{proto=="www."?"http://www.":proto}#{url}"
+            %(#{leading}<a class="external" href="#{ERB::Util.html_escape href}">#{ERB::Util.html_escape content}</a>#{post}).html_safe
+          end
+        end
+      end
+
+      # Destructively remplaces email addresses into clickable links
+      def auto_mailto!(text)
+        text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
+          mail = $1
+          if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
+            mail
+          else
+            %(<a class="email" href="mailto:#{ERB::Util.html_escape mail}">#{ERB::Util.html_escape mail}</a>).html_safe
+          end
+        end
+      end      
+    end
+
     # Default formatter module
     module NullFormatter
       class Formatter
         include ActionView::Helpers::TagHelper
         include ActionView::Helpers::TextHelper
         include ActionView::Helpers::UrlHelper
+        include Redmine::WikiFormatting::LinksHelper
 
         def initialize(text)
           @text = text
         end
 
         def to_html(*args)
-          simple_format(auto_link(CGI::escapeHTML(@text)))
+          t = CGI::escapeHTML(@text)
+          auto_link!(t)
+          auto_mailto!(t)
+          simple_format(t, {}, :sanitize => false)
         end
       end