comparison lib/redmine/wiki_formatting/textile/formatter.rb @ 909:cbb26bc654de redmine-1.3

Update to Redmine 1.3-stable branch (Redmine SVN rev 8964)
author Chris Cannam
date Fri, 24 Feb 2012 19:09:32 +0000
parents cbce1fd3b1b7
children 433d4f72a19b
comparison
equal deleted inserted replaced
908:c6c2cbd0afee 909:cbb26bc654de
1 # Redmine - project management software 1 # Redmine - project management software
2 # Copyright (C) 2006-2010 Jean-Philippe Lang 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 # 3 #
4 # This program is free software; you can redistribute it and/or 4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License 5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2 6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version. 7 # of the License, or (at your option) any later version.
8 # 8 #
9 # This program is distributed in the hope that it will be useful, 9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details. 12 # GNU General Public License for more details.
13 # 13 #
14 # You should have received a copy of the GNU General Public License 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 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. 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 require 'redcloth3' 18 require 'redcloth3'
19 require 'digest/md5'
19 20
20 module Redmine 21 module Redmine
21 module WikiFormatting 22 module WikiFormatting
22 module Textile 23 module Textile
23 class Formatter < RedCloth3 24 class Formatter < RedCloth3
24 include ActionView::Helpers::TagHelper 25 include ActionView::Helpers::TagHelper
25 26
26 # auto_link rule after textile rules so that it doesn't break !image_url! tags 27 # auto_link rule after textile rules so that it doesn't break !image_url! tags
27 RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto] 28 RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto]
28 29
29 def initialize(*args) 30 def initialize(*args)
30 super 31 super
31 self.hard_breaks=true 32 self.hard_breaks=true
32 self.no_span_caps=true 33 self.no_span_caps=true
33 self.filter_styles=true 34 self.filter_styles=true
34 end 35 end
35 36
36 def to_html(*rules) 37 def to_html(*rules)
37 @toc = [] 38 @toc = []
38 super(*RULES).to_s 39 super(*RULES).to_s
39 end 40 end
40 41
42 def get_section(index)
43 section = extract_sections(index)[1]
44 hash = Digest::MD5.hexdigest(section)
45 return section, hash
46 end
47
48 def update_section(index, update, hash=nil)
49 t = extract_sections(index)
50 if hash.present? && hash != Digest::MD5.hexdigest(t[1])
51 raise Redmine::WikiFormatting::StaleSectionError
52 end
53 t[1] = update unless t[1].blank?
54 t.reject(&:blank?).join "\n\n"
55 end
56
57 def extract_sections(index)
58 @pre_list = []
59 text = self.dup
60 rip_offtags text, false, false
61 before = ''
62 s = ''
63 after = ''
64 i = 0
65 l = 1
66 started = false
67 ended = false
68 text.scan(/(((?:.*?)(\A|\r?\n\r?\n))(h(\d+)(#{A}#{C})\.(?::(\S+))? (.*?)$)|.*)/m).each do |all, content, lf, heading, level|
69 if heading.nil?
70 if ended
71 after << all
72 elsif started
73 s << all
74 else
75 before << all
76 end
77 break
78 end
79 i += 1
80 if ended
81 after << all
82 elsif i == index
83 l = level.to_i
84 before << content
85 s << heading
86 started = true
87 elsif i > index
88 s << content
89 if level.to_i > l
90 s << heading
91 else
92 after << heading
93 ended = true
94 end
95 else
96 before << all
97 end
98 end
99 sections = [before.strip, s.strip, after.strip]
100 sections.each {|section| smooth_offtags_without_code_highlighting section}
101 sections
102 end
103
41 private 104 private
42 105
43 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet. 106 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
44 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a> 107 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
45 def hard_break( text ) 108 def hard_break( text )
46 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks 109 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
47 end 110 end
48 111
112 alias :smooth_offtags_without_code_highlighting :smooth_offtags
49 # Patch to add code highlighting support to RedCloth 113 # Patch to add code highlighting support to RedCloth
50 def smooth_offtags( text ) 114 def smooth_offtags( text )
51 unless @pre_list.empty? 115 unless @pre_list.empty?
52 ## replace <pre> content 116 ## replace <pre> content
53 text.gsub!(/<redpre#(\d+)>/) do 117 text.gsub!(/<redpre#(\d+)>/) do
54 content = @pre_list[$1.to_i] 118 content = @pre_list[$1.to_i]
55 if content.match(/<code\s+class="(\w+)">\s?(.+)/m) 119 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
56 content = "<code class=\"#{$1} syntaxhl\">" + 120 content = "<code class=\"#{$1} syntaxhl\">" +
57 Redmine::SyntaxHighlighting.highlight_by_language($2, $1) 121 Redmine::SyntaxHighlighting.highlight_by_language($2, $1)
58 end 122 end
59 content 123 content
60 end 124 end
61 end 125 end
62 end 126 end
63 127
64 AUTO_LINK_RE = %r{ 128 AUTO_LINK_RE = %r{
65 ( # leading text 129 ( # leading text
66 <\w+.*?>| # leading HTML tag, or 130 <\w+.*?>| # leading HTML tag, or
67 [^=<>!:'"/]| # leading punctuation, or 131 [^=<>!:'"/]| # leading punctuation, or
68 ^ # beginning of line 132 ^ # beginning of line
69 ) 133 )
70 ( 134 (
71 (?:https?://)| # protocol spec, or 135 (?:https?://)| # protocol spec, or
72 (?:s?ftps?://)| 136 (?:s?ftps?://)|
77 (\/)? # slash 141 (\/)? # slash
78 ) 142 )
79 ((?:&gt;)?|[^\w\=\/;\(\)]*?) # post 143 ((?:&gt;)?|[^\w\=\/;\(\)]*?) # post
80 (?=<|\s|$) 144 (?=<|\s|$)
81 }x unless const_defined?(:AUTO_LINK_RE) 145 }x unless const_defined?(:AUTO_LINK_RE)
82 146
83 # Turns all urls into clickable links (code from Rails). 147 # Turns all urls into clickable links (code from Rails).
84 def inline_auto_link(text) 148 def inline_auto_link(text)
85 text.gsub!(AUTO_LINK_RE) do 149 text.gsub!(AUTO_LINK_RE) do
86 all, leading, proto, url, post = $&, $1, $2, $3, $6 150 all, leading, proto, url, post = $&, $1, $2, $3, $6
87 if leading =~ /<a\s/i || leading =~ /![<>=]?/ 151 if leading =~ /<a\s/i || leading =~ /![<>=]?/
98 tag = content_tag('a', proto + url, :href => "#{proto=="www."?"http://www.":proto}#{url}", :class => 'external') 162 tag = content_tag('a', proto + url, :href => "#{proto=="www."?"http://www.":proto}#{url}", :class => 'external')
99 %(#{leading}#{tag}#{post}) 163 %(#{leading}#{tag}#{post})
100 end 164 end
101 end 165 end
102 end 166 end
103 167
104 # Turns all email addresses into clickable links (code from Rails). 168 # Turns all email addresses into clickable links (code from Rails).
105 def inline_auto_mailto(text) 169 def inline_auto_mailto(text)
106 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do 170 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
107 mail = $1 171 mail = $1
108 if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/) 172 if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)