##// END OF EJS Templates
Fixed: Deselecting textile text formatting causes interning empty string errors (#4867)....
Jean-Philippe Lang -
r3332:0fcc436f226f
parent child
Show More
@@ -1,131 +1,131
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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 module Redmine
18 module Redmine
19 module WikiFormatting
19 module WikiFormatting
20 @@formatters = {}
20 @@formatters = {}
21
21
22 class << self
22 class << self
23 def map
23 def map
24 yield self
24 yield self
25 end
25 end
26
26
27 def register(name, formatter, helper)
27 def register(name, formatter, helper)
28 raise ArgumentError, "format name '#{name}' is already taken" if @@formatters[name.to_sym]
28 raise ArgumentError, "format name '#{name}' is already taken" if @@formatters[name.to_s]
29 @@formatters[name.to_sym] = {:formatter => formatter, :helper => helper}
29 @@formatters[name.to_s] = {:formatter => formatter, :helper => helper}
30 end
30 end
31
31
32 def formatter_for(name)
32 def formatter_for(name)
33 entry = @@formatters[name.to_sym]
33 entry = @@formatters[name.to_s]
34 (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter
34 (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter
35 end
35 end
36
36
37 def helper_for(name)
37 def helper_for(name)
38 entry = @@formatters[name.to_sym]
38 entry = @@formatters[name.to_s]
39 (entry && entry[:helper]) || Redmine::WikiFormatting::NullFormatter::Helper
39 (entry && entry[:helper]) || Redmine::WikiFormatting::NullFormatter::Helper
40 end
40 end
41
41
42 def format_names
42 def format_names
43 @@formatters.keys.map
43 @@formatters.keys.map
44 end
44 end
45
45
46 def to_html(format, text, options = {}, &block)
46 def to_html(format, text, options = {}, &block)
47 text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, options[:object], options[:attribute])
47 text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, options[:object], options[:attribute])
48 # Text retrieved from the cache store may be frozen
48 # Text retrieved from the cache store may be frozen
49 # We need to dup it so we can do in-place substitutions with gsub!
49 # We need to dup it so we can do in-place substitutions with gsub!
50 cache_store.fetch cache_key do
50 cache_store.fetch cache_key do
51 formatter_for(format).new(text).to_html
51 formatter_for(format).new(text).to_html
52 end.dup
52 end.dup
53 else
53 else
54 formatter_for(format).new(text).to_html
54 formatter_for(format).new(text).to_html
55 end
55 end
56 if block_given?
56 if block_given?
57 execute_macros(text, block)
57 execute_macros(text, block)
58 end
58 end
59 text
59 text
60 end
60 end
61
61
62 # Returns a cache key for the given text +format+, +object+ and +attribute+ or nil if no caching should be done
62 # Returns a cache key for the given text +format+, +object+ and +attribute+ or nil if no caching should be done
63 def cache_key_for(format, object, attribute)
63 def cache_key_for(format, object, attribute)
64 if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank?
64 if object && attribute && !object.new_record? && object.respond_to?(:updated_on) && !format.blank?
65 "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}"
65 "formatted_text/#{format}/#{object.class.model_name.cache_key}/#{object.id}-#{attribute}-#{object.updated_on.to_s(:number)}"
66 end
66 end
67 end
67 end
68
68
69 # Returns the cache store used to cache HTML output
69 # Returns the cache store used to cache HTML output
70 def cache_store
70 def cache_store
71 ActionController::Base.cache_store
71 ActionController::Base.cache_store
72 end
72 end
73
73
74 MACROS_RE = /
74 MACROS_RE = /
75 (!)? # escaping
75 (!)? # escaping
76 (
76 (
77 \{\{ # opening tag
77 \{\{ # opening tag
78 ([\w]+) # macro name
78 ([\w]+) # macro name
79 (\(([^\}]*)\))? # optional arguments
79 (\(([^\}]*)\))? # optional arguments
80 \}\} # closing tag
80 \}\} # closing tag
81 )
81 )
82 /x unless const_defined?(:MACROS_RE)
82 /x unless const_defined?(:MACROS_RE)
83
83
84 # Macros substitution
84 # Macros substitution
85 def execute_macros(text, macros_runner)
85 def execute_macros(text, macros_runner)
86 text.gsub!(MACROS_RE) do
86 text.gsub!(MACROS_RE) do
87 esc, all, macro = $1, $2, $3.downcase
87 esc, all, macro = $1, $2, $3.downcase
88 args = ($5 || '').split(',').each(&:strip)
88 args = ($5 || '').split(',').each(&:strip)
89 if esc.nil?
89 if esc.nil?
90 begin
90 begin
91 macros_runner.call(macro, args)
91 macros_runner.call(macro, args)
92 rescue => e
92 rescue => e
93 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
93 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
94 end || all
94 end || all
95 else
95 else
96 all
96 all
97 end
97 end
98 end
98 end
99 end
99 end
100 end
100 end
101
101
102 # Default formatter module
102 # Default formatter module
103 module NullFormatter
103 module NullFormatter
104 class Formatter
104 class Formatter
105 include ActionView::Helpers::TagHelper
105 include ActionView::Helpers::TagHelper
106 include ActionView::Helpers::TextHelper
106 include ActionView::Helpers::TextHelper
107 include ActionView::Helpers::UrlHelper
107 include ActionView::Helpers::UrlHelper
108
108
109 def initialize(text)
109 def initialize(text)
110 @text = text
110 @text = text
111 end
111 end
112
112
113 def to_html(*args)
113 def to_html(*args)
114 simple_format(auto_link(CGI::escapeHTML(@text)))
114 simple_format(auto_link(CGI::escapeHTML(@text)))
115 end
115 end
116 end
116 end
117
117
118 module Helper
118 module Helper
119 def wikitoolbar_for(field_id)
119 def wikitoolbar_for(field_id)
120 end
120 end
121
121
122 def heads_for_wiki_formatter
122 def heads_for_wiki_formatter
123 end
123 end
124
124
125 def initial_page_content(page)
125 def initial_page_content(page)
126 page.pretty_title.to_s
126 page.pretty_title.to_s
127 end
127 end
128 end
128 end
129 end
129 end
130 end
130 end
131 end
131 end
@@ -1,35 +1,45
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 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 File.dirname(__FILE__) + '/../../../test_helper'
18 require File.dirname(__FILE__) + '/../../../test_helper'
19
19
20 class Redmine::WikiFormattingTest < ActiveSupport::TestCase
20 class Redmine::WikiFormattingTest < ActiveSupport::TestCase
21
21
22 def test_textile_formatter
23 assert_equal Redmine::WikiFormatting::Textile::Formatter, Redmine::WikiFormatting.formatter_for('textile')
24 assert_equal Redmine::WikiFormatting::Textile::Helper, Redmine::WikiFormatting.helper_for('textile')
25 end
26
27 def test_null_formatter
28 assert_equal Redmine::WikiFormatting::NullFormatter::Formatter, Redmine::WikiFormatting.formatter_for('')
29 assert_equal Redmine::WikiFormatting::NullFormatter::Helper, Redmine::WikiFormatting.helper_for('')
30 end
31
22 def test_should_link_urls_and_email_addresses
32 def test_should_link_urls_and_email_addresses
23 raw = <<-DIFF
33 raw = <<-DIFF
24 This is a sample *text* with a link: http://www.redmine.org
34 This is a sample *text* with a link: http://www.redmine.org
25 and an email address foo@example.net
35 and an email address foo@example.net
26 DIFF
36 DIFF
27
37
28 expected = <<-EXPECTED
38 expected = <<-EXPECTED
29 <p>This is a sample *text* with a link: <a href="http://www.redmine.org">http://www.redmine.org</a><br />
39 <p>This is a sample *text* with a link: <a href="http://www.redmine.org">http://www.redmine.org</a><br />
30 and an email address <a href="mailto:foo@example.net">foo@example.net</a></p>
40 and an email address <a href="mailto:foo@example.net">foo@example.net</a></p>
31 EXPECTED
41 EXPECTED
32
42
33 assert_equal expected.gsub(%r{[\r\n\t]}, ''), Redmine::WikiFormatting::NullFormatter::Formatter.new(raw).to_html.gsub(%r{[\r\n\t]}, '')
43 assert_equal expected.gsub(%r{[\r\n\t]}, ''), Redmine::WikiFormatting::NullFormatter::Formatter.new(raw).to_html.gsub(%r{[\r\n\t]}, '')
34 end
44 end
35 end
45 end
General Comments 0
You need to be logged in to leave comments. Login now