##// END OF EJS Templates
Enables footnote support with Markdown (#16072)....
Jean-Philippe Lang -
r14246:dfa69b2eb0f8
parent child
Show More
@@ -1,137 +1,138
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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 'cgi'
18 require 'cgi'
19
19
20 module Redmine
20 module Redmine
21 module WikiFormatting
21 module WikiFormatting
22 module Markdown
22 module Markdown
23 class HTML < Redcarpet::Render::HTML
23 class HTML < Redcarpet::Render::HTML
24 include ActionView::Helpers::TagHelper
24 include ActionView::Helpers::TagHelper
25
25
26 def link(link, title, content)
26 def link(link, title, content)
27 css = nil
27 css = nil
28 unless link && link.starts_with?('/')
28 unless link && link.starts_with?('/')
29 css = 'external'
29 css = 'external'
30 end
30 end
31 content_tag('a', content.to_s.html_safe, :href => link, :title => title, :class => css)
31 content_tag('a', content.to_s.html_safe, :href => link, :title => title, :class => css)
32 end
32 end
33
33
34 def block_code(code, language)
34 def block_code(code, language)
35 if language.present?
35 if language.present?
36 "<pre><code class=\"#{CGI.escapeHTML language} syntaxhl\">" +
36 "<pre><code class=\"#{CGI.escapeHTML language} syntaxhl\">" +
37 Redmine::SyntaxHighlighting.highlight_by_language(code, language) +
37 Redmine::SyntaxHighlighting.highlight_by_language(code, language) +
38 "</code></pre>"
38 "</code></pre>"
39 else
39 else
40 "<pre>" + CGI.escapeHTML(code) + "</pre>"
40 "<pre>" + CGI.escapeHTML(code) + "</pre>"
41 end
41 end
42 end
42 end
43 end
43 end
44
44
45 class Formatter
45 class Formatter
46 def initialize(text)
46 def initialize(text)
47 @text = text
47 @text = text
48 end
48 end
49
49
50 def to_html(*args)
50 def to_html(*args)
51 html = formatter.render(@text)
51 html = formatter.render(@text)
52 # restore wiki links eg. [[Foo]]
52 # restore wiki links eg. [[Foo]]
53 html.gsub!(%r{\[<a href="(.*?)">(.*?)</a>\]}) do
53 html.gsub!(%r{\[<a href="(.*?)">(.*?)</a>\]}) do
54 "[[#{$2}]]"
54 "[[#{$2}]]"
55 end
55 end
56 # restore Redmine links with double-quotes, eg. version:"1.0"
56 # restore Redmine links with double-quotes, eg. version:"1.0"
57 html.gsub!(/(\w):&quot;(.+?)&quot;/) do
57 html.gsub!(/(\w):&quot;(.+?)&quot;/) do
58 "#{$1}:\"#{$2}\""
58 "#{$1}:\"#{$2}\""
59 end
59 end
60 html
60 html
61 end
61 end
62
62
63 def get_section(index)
63 def get_section(index)
64 section = extract_sections(index)[1]
64 section = extract_sections(index)[1]
65 hash = Digest::MD5.hexdigest(section)
65 hash = Digest::MD5.hexdigest(section)
66 return section, hash
66 return section, hash
67 end
67 end
68
68
69 def update_section(index, update, hash=nil)
69 def update_section(index, update, hash=nil)
70 t = extract_sections(index)
70 t = extract_sections(index)
71 if hash.present? && hash != Digest::MD5.hexdigest(t[1])
71 if hash.present? && hash != Digest::MD5.hexdigest(t[1])
72 raise Redmine::WikiFormatting::StaleSectionError
72 raise Redmine::WikiFormatting::StaleSectionError
73 end
73 end
74 t[1] = update unless t[1].blank?
74 t[1] = update unless t[1].blank?
75 t.reject(&:blank?).join "\n\n"
75 t.reject(&:blank?).join "\n\n"
76 end
76 end
77
77
78 def extract_sections(index)
78 def extract_sections(index)
79 sections = ['', '', '']
79 sections = ['', '', '']
80 offset = 0
80 offset = 0
81 i = 0
81 i = 0
82 l = 1
82 l = 1
83 inside_pre = false
83 inside_pre = false
84 @text.split(/(^(?:.+\r?\n\r?(?:\=+|\-+)|#+.+|~~~.*)\s*$)/).each do |part|
84 @text.split(/(^(?:.+\r?\n\r?(?:\=+|\-+)|#+.+|~~~.*)\s*$)/).each do |part|
85 level = nil
85 level = nil
86 if part =~ /\A~{3,}(\S+)?\s*$/
86 if part =~ /\A~{3,}(\S+)?\s*$/
87 if $1
87 if $1
88 if !inside_pre
88 if !inside_pre
89 inside_pre = true
89 inside_pre = true
90 end
90 end
91 else
91 else
92 inside_pre = !inside_pre
92 inside_pre = !inside_pre
93 end
93 end
94 elsif inside_pre
94 elsif inside_pre
95 # nop
95 # nop
96 elsif part =~ /\A(#+).+/
96 elsif part =~ /\A(#+).+/
97 level = $1.size
97 level = $1.size
98 elsif part =~ /\A.+\r?\n\r?(\=+|\-+)\s*$/
98 elsif part =~ /\A.+\r?\n\r?(\=+|\-+)\s*$/
99 level = $1.include?('=') ? 1 : 2
99 level = $1.include?('=') ? 1 : 2
100 end
100 end
101 if level
101 if level
102 i += 1
102 i += 1
103 if offset == 0 && i == index
103 if offset == 0 && i == index
104 # entering the requested section
104 # entering the requested section
105 offset = 1
105 offset = 1
106 l = level
106 l = level
107 elsif offset == 1 && i > index && level <= l
107 elsif offset == 1 && i > index && level <= l
108 # leaving the requested section
108 # leaving the requested section
109 offset = 2
109 offset = 2
110 end
110 end
111 end
111 end
112 sections[offset] << part
112 sections[offset] << part
113 end
113 end
114 sections.map(&:strip)
114 sections.map(&:strip)
115 end
115 end
116
116
117 private
117 private
118
118
119 def formatter
119 def formatter
120 @@formatter ||= Redcarpet::Markdown.new(
120 @@formatter ||= Redcarpet::Markdown.new(
121 Redmine::WikiFormatting::Markdown::HTML.new(
121 Redmine::WikiFormatting::Markdown::HTML.new(
122 :filter_html => true,
122 :filter_html => true,
123 :hard_wrap => true
123 :hard_wrap => true
124 ),
124 ),
125 :autolink => true,
125 :autolink => true,
126 :fenced_code_blocks => true,
126 :fenced_code_blocks => true,
127 :space_after_headers => true,
127 :space_after_headers => true,
128 :tables => true,
128 :tables => true,
129 :strikethrough => true,
129 :strikethrough => true,
130 :superscript => true,
130 :superscript => true,
131 :no_intra_emphasis => true
131 :no_intra_emphasis => true,
132 :footnotes => true
132 )
133 )
133 end
134 end
134 end
135 end
135 end
136 end
136 end
137 end
137 end
138 end
General Comments 0
You need to be logged in to leave comments. Login now