File: lib/redmine/wiki_formatting.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: Redmine#18
  module: WikiFormatting#19
has properties
module method: map #25
module method: register / 3 #29
module method: formatter #34
module method: formatter_for / 1 #38
module method: helper_for / 1 #43
module method: format_names #48
module method: to_html / 3 #52
module method: supports_section_edit? #66
module method: cache_key_for / 3 #71
module method: cache_store #78
  class: StaleSectionError#20
inherits from
  Exception ( Builtin-Module )
  module: LinksHelper#83
has properties
constant: AUTO_LINK_RE #84
method: auto_link! / 1 #104
method: auto_mailto! / 1 #125
  module: NullFormatter#138
  class: Formatter#139
includes
  TagHelper ( ActionView::Helpers )
  TextHelper ( ActionView::Helpers )
  UrlHelper ( ActionView::Helpers )
  LinksHelper ( Redmine::WikiFormatting )
inherits from
  Object ( Builtin-Module )
has properties
method: initialize / 1 #145
method: to_html / 1 #149
  module: Helper#157
has properties
method: wikitoolbar_for / 1 #158
method: heads_for_wiki_formatter #161
method: initial_page_content / 1 #164

Code

   1  # Redmine - project management software
   2  # Copyright (C) 2006-2011  Jean-Philippe Lang
   3  #
   4  # This program is free software; you can redistribute it and/or
   5  # modify it under the terms of the GNU General Public License
   6  # as published by the Free Software Foundation; either version 2
   7  # of the License, or (at your option) any later version.
   8  #
   9  # This program is distributed in the hope that it will be useful,
  10  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  # GNU General Public License for more details.
  13  #
  14  # You should have received a copy of the GNU General Public License
  15  # along with this program; if not, write to the Free Software
  16  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  17 
  18  module Redmine
  19    module WikiFormatting
  20      class StaleSectionError < Exception; end
  21 
  22      @@formatters = {}
  23 
  24      class << self
  25        def map
  26          yield self
  27        end
  28 
  29        def register(name, formatter, helper)
  30          raise ArgumentError, "format name '#{name}' is already taken" if @@formatters[name.to_s]
  31          @@formatters[name.to_s] = {:formatter => formatter, :helper => helper}
  32        end
  33 
  34        def formatter
  35          formatter_for(Setting.text_formatting)
  36        end
  37 
  38        def formatter_for(name)
  39          entry = @@formatters[name.to_s]
  40          (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter
  41        end
  42 
  43        def helper_for(name)
  44          entry = @@formatters[name.to_s]
  45          (entry && entry[:helper]) || Redmine::WikiFormatting::NullFormatter::Helper
  46        end
  47 
  48        def format_names
  49          @@formatters.keys.map
  50        end
  51 
  52        def to_html(format, text, options = {})
  53          text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, options[:object], options[:attribute])
  54            # Text retrieved from the cache store may be frozen
  55            # We need to dup it so we can do in-place substitutions with gsub!
  56            cache_store.fetch cache_key do
  57              formatter_for(format).new(text).to_html
  58            end.dup
  59          else
  60            formatter_for(format).new(text).to_html
  61          end
  62          text
  63        end
  64 
  65        # Returns true if the text formatter supports single section edit
  66        def supports_section_edit?
  67          (formatter.instance_methods & ['update_section', :update_section]).any?
  68        end
  69 
  70        # Returns a cache key for the given text +format+, +object+ and +attribute+ or nil if no caching should be done
  71        def cache_key_for(format, object, attribute)
  72          if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank?
  73            "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}"
  74          end
  75        end
  76 
  77        # Returns the cache store used to cache HTML output
  78        def cache_store
  79          ActionController::Base.cache_store
  80        end
  81      end
  82 
  83      module LinksHelper
  84        AUTO_LINK_RE = %r{
  85                        (                          # leading text
  86                          <\w+.*?>|                # leading HTML tag, or
  87                          [^=<>!:'"/]|             # leading punctuation, or
  88                          ^                        # beginning of line
  89                        )
  90                        (
  91                          (?:https?://)|           # protocol spec, or
  92                          (?:s?ftps?://)|
  93                          (?:www\.)                # www.*
  94                        )
  95                        (
  96                          (\S+?)                   # url
  97                          (\/)?                    # slash
  98                        )
  99                        ((?:&gt;)?|[^\w\=\/;\(\)]*?)               # post
 100                        (?=<|\s|$)
 101                       }x unless const_defined?(:AUTO_LINK_RE)
 102 
 103        # Destructively remplaces urls into clickable links
 104        def auto_link!(text)
 105          text.gsub!(AUTO_LINK_RE) do
 106            all, leading, proto, url, post = $&, $1, $2, $3, $6
 107            if leading =~ /<a\s/i || leading =~ /![<>=]?/
 108              # don't replace URL's that are already linked
 109              # and URL's prefixed with ! !> !< != (textile images)
 110              all
 111            else
 112              # Idea below : an URL with unbalanced parethesis and
 113              # ending by ')' is put into external parenthesis
 114              if ( url[-1]==?) and ((url.count("(") - url.count(")")) < 0 ) )
 115                url=url[0..-2] # discard closing parenth from url
 116                post = ")"+post # add closing parenth to post
 117              end
 118              tag = content_tag('a', proto + url, :href => "#{proto=="www."?"http://www.":proto}#{url}", :class => 'external')
 119              %(#{leading}#{tag}#{post})
 120            end
 121          end
 122        end
 123 
 124        # Destructively remplaces email addresses into clickable links
 125        def auto_mailto!(text)
 126          text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
 127            mail = $1
 128            if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
 129              mail
 130            else
 131              content_tag('a', mail, :href => "mailto:#{mail}", :class => "email")
 132            end
 133          end
 134        end      
 135      end
 136 
 137      # Default formatter module
 138      module NullFormatter
 139        class Formatter
 140          include ActionView::Helpers::TagHelper
 141          include ActionView::Helpers::TextHelper
 142          include ActionView::Helpers::UrlHelper
 143          include Redmine::WikiFormatting::LinksHelper
 144 
 145          def initialize(text)
 146            @text = text
 147          end
 148 
 149          def to_html(*args)
 150            t = CGI::escapeHTML(@text)
 151            auto_link!(t)
 152            auto_mailto!(t)
 153            simple_format(t)
 154          end
 155        end
 156 
 157        module Helper
 158          def wikitoolbar_for(field_id)
 159          end
 160 
 161          def heads_for_wiki_formatter
 162          end
 163 
 164          def initial_page_content(page)
 165            page.pretty_title.to_s
 166          end
 167        end
 168      end
 169    end
 170  end