##// END OF EJS Templates
Added:...
Jean-Philippe Lang -
r1139:05823373724e
parent child
Show More
@@ -1,161 +1,168
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 'redcloth'
18 require 'redcloth'
19 require 'coderay'
19 require 'coderay'
20
20
21 module Redmine
21 module Redmine
22 module WikiFormatting
22 module WikiFormatting
23
23
24 private
24 private
25
25
26 class TextileFormatter < RedCloth
26 class TextileFormatter < RedCloth
27
27
28 # auto_link rule after textile rules so that it doesn't break !image_url! tags
28 # auto_link rule after textile rules so that it doesn't break !image_url! tags
29 RULES = [:textile, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
29 RULES = [:textile, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
30
30
31 def initialize(*args)
31 def initialize(*args)
32 super
32 super
33 self.hard_breaks=true
33 self.hard_breaks=true
34 self.no_span_caps=true
34 self.no_span_caps=true
35 end
35 end
36
36
37 def to_html(*rules, &block)
37 def to_html(*rules, &block)
38 @toc = []
38 @toc = []
39 @macros_runner = block
39 @macros_runner = block
40 super(*RULES).to_s
40 super(*RULES).to_s
41 end
41 end
42
42
43 private
43 private
44
44
45 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
45 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
46 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
46 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
47 def hard_break( text )
47 def hard_break( text )
48 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
48 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
49 end
49 end
50
50
51 # Patch to add code highlighting support to RedCloth
51 # Patch to add code highlighting support to RedCloth
52 def smooth_offtags( text )
52 def smooth_offtags( text )
53 unless @pre_list.empty?
53 unless @pre_list.empty?
54 ## replace <pre> content
54 ## replace <pre> content
55 text.gsub!(/<redpre#(\d+)>/) do
55 text.gsub!(/<redpre#(\d+)>/) do
56 content = @pre_list[$1.to_i]
56 content = @pre_list[$1.to_i]
57 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
57 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
58 content = "<code class=\"#{$1} CodeRay\">" +
58 content = "<code class=\"#{$1} CodeRay\">" +
59 CodeRay.scan($2, $1).html(:escape => false, :line_numbers => :inline)
59 CodeRay.scan($2, $1).html(:escape => false, :line_numbers => :inline)
60 end
60 end
61 content
61 content
62 end
62 end
63 end
63 end
64 end
64 end
65
65
66 # Patch to add 'table of content' support to RedCloth
66 # Patch to add 'table of content' support to RedCloth
67 def textile_p_withtoc(tag, atts, cite, content)
67 def textile_p_withtoc(tag, atts, cite, content)
68 if tag =~ /^h(\d)$/
68 if tag =~ /^h(\d)$/
69 @toc << [$1.to_i, content]
69 @toc << [$1.to_i, content]
70 end
70 end
71 content = "<a name=\"#{@toc.length}\" class=\"wiki-page\"></a>" + content
71 content = "<a name=\"#{@toc.length}\" class=\"wiki-page\"></a>" + content
72 textile_p(tag, atts, cite, content)
72 textile_p(tag, atts, cite, content)
73 end
73 end
74
74
75 alias :textile_h1 :textile_p_withtoc
75 alias :textile_h1 :textile_p_withtoc
76 alias :textile_h2 :textile_p_withtoc
76 alias :textile_h2 :textile_p_withtoc
77 alias :textile_h3 :textile_p_withtoc
77 alias :textile_h3 :textile_p_withtoc
78
78
79 def inline_toc(text)
79 def inline_toc(text)
80 text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
80 text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
81 div_class = 'toc'
81 div_class = 'toc'
82 div_class << ' right' if $1 == '>'
82 div_class << ' right' if $1 == '>'
83 div_class << ' left' if $1 == '<'
83 div_class << ' left' if $1 == '<'
84 out = "<div class=\"#{div_class}\">"
84 out = "<div class=\"#{div_class}\">"
85 @toc.each_with_index do |heading, index|
85 @toc.each_with_index do |heading, index|
86 # remove wiki links from the item
86 # remove wiki links from the item
87 toc_item = heading.last.gsub(/(\[\[|\]\])/, '')
87 toc_item = heading.last.gsub(/(\[\[|\]\])/, '')
88 out << "<a href=\"##{index+1}\" class=\"heading#{heading.first}\">#{toc_item}</a>"
88 out << "<a href=\"##{index+1}\" class=\"heading#{heading.first}\">#{toc_item}</a>"
89 end
89 end
90 out << '</div>'
90 out << '</div>'
91 out
91 out
92 end
92 end
93 end
93 end
94
94
95 MACROS_RE = /
95 MACROS_RE = /
96 (!)? # escaping
97 (
96 \{\{ # opening tag
98 \{\{ # opening tag
97 ([\w]+) # macro name
99 ([\w]+) # macro name
98 (\(([^\}]*)\))? # optional arguments
100 (\(([^\}]*)\))? # optional arguments
99 \}\} # closing tag
101 \}\} # closing tag
102 )
100 /x unless const_defined?(:MACROS_RE)
103 /x unless const_defined?(:MACROS_RE)
101
104
102 def inline_macros(text)
105 def inline_macros(text)
103 text.gsub!(MACROS_RE) do
106 text.gsub!(MACROS_RE) do
104 all, macro = $&, $1.downcase
107 esc, all, macro = $1, $2, $3.downcase
105 args = ($3 || '').split(',').each(&:strip)
108 args = ($5 || '').split(',').each(&:strip)
106 begin
109 if esc.nil?
107 @macros_runner.call(macro, args)
110 begin
108 rescue => e
111 @macros_runner.call(macro, args)
109 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
112 rescue => e
110 end || all
113 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
114 end || all
115 else
116 all
117 end
111 end
118 end
112 end
119 end
113
120
114 AUTO_LINK_RE = %r{
121 AUTO_LINK_RE = %r{
115 ( # leading text
122 ( # leading text
116 <\w+.*?>| # leading HTML tag, or
123 <\w+.*?>| # leading HTML tag, or
117 [^=<>!:'"/]| # leading punctuation, or
124 [^=<>!:'"/]| # leading punctuation, or
118 ^ # beginning of line
125 ^ # beginning of line
119 )
126 )
120 (
127 (
121 (?:https?://)| # protocol spec, or
128 (?:https?://)| # protocol spec, or
122 (?:www\.) # www.*
129 (?:www\.) # www.*
123 )
130 )
124 (
131 (
125 (\S+?) # url
132 (\S+?) # url
126 (\/)? # slash
133 (\/)? # slash
127 )
134 )
128 ([^\w\=\/;]*?) # post
135 ([^\w\=\/;]*?) # post
129 (?=<|\s|$)
136 (?=<|\s|$)
130 }x unless const_defined?(:AUTO_LINK_RE)
137 }x unless const_defined?(:AUTO_LINK_RE)
131
138
132 # Turns all urls into clickable links (code from Rails).
139 # Turns all urls into clickable links (code from Rails).
133 def inline_auto_link(text)
140 def inline_auto_link(text)
134 text.gsub!(AUTO_LINK_RE) do
141 text.gsub!(AUTO_LINK_RE) do
135 all, leading, proto, url, post = $&, $1, $2, $3, $6
142 all, leading, proto, url, post = $&, $1, $2, $3, $6
136 if leading =~ /<a\s/i || leading =~ /![<>=]?/
143 if leading =~ /<a\s/i || leading =~ /![<>=]?/
137 # don't replace URL's that are already linked
144 # don't replace URL's that are already linked
138 # and URL's prefixed with ! !> !< != (textile images)
145 # and URL's prefixed with ! !> !< != (textile images)
139 all
146 all
140 else
147 else
141 %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
148 %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
142 end
149 end
143 end
150 end
144 end
151 end
145
152
146 # Turns all email addresses into clickable links (code from Rails).
153 # Turns all email addresses into clickable links (code from Rails).
147 def inline_auto_mailto(text)
154 def inline_auto_mailto(text)
148 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
155 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
149 text = $1
156 text = $1
150 %{<a href="mailto:#{$1}" class="email">#{text}</a>}
157 %{<a href="mailto:#{$1}" class="email">#{text}</a>}
151 end
158 end
152 end
159 end
153 end
160 end
154
161
155 public
162 public
156
163
157 def self.to_html(text, options = {}, &block)
164 def self.to_html(text, options = {}, &block)
158 TextileFormatter.new(text).to_html(&block)
165 TextileFormatter.new(text).to_html(&block)
159 end
166 end
160 end
167 end
161 end
168 end
@@ -1,81 +1,98
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 module Macros
20 module Macros
21 module Definitions
21 module Definitions
22 def exec_macro(name, obj, args)
22 def exec_macro(name, obj, args)
23 method_name = "macro_#{name}"
23 method_name = "macro_#{name}"
24 send(method_name, obj, args) if respond_to?(method_name)
24 send(method_name, obj, args) if respond_to?(method_name)
25 end
25 end
26 end
26 end
27
27
28 @@available_macros = {}
28 @@available_macros = {}
29
29
30 class << self
30 class << self
31 # Called with a block to define additional macros.
31 # Called with a block to define additional macros.
32 # Macro blocks accept 2 arguments:
32 # Macro blocks accept 2 arguments:
33 # * obj: the object that is rendered
33 # * obj: the object that is rendered
34 # * args: macro arguments
34 # * args: macro arguments
35 #
35 #
36 # Plugins can use this method to define new macros:
36 # Plugins can use this method to define new macros:
37 #
37 #
38 # Redmine::WikiFormatting::Macros.register do
38 # Redmine::WikiFormatting::Macros.register do
39 # desc "This is my macro"
39 # desc "This is my macro"
40 # macro :my_macro do |obj, args|
40 # macro :my_macro do |obj, args|
41 # "My macro output"
41 # "My macro output"
42 # end
42 # end
43 # end
43 # end
44 def register(&block)
44 def register(&block)
45 class_eval(&block) if block_given?
45 class_eval(&block) if block_given?
46 end
46 end
47
47
48 private
48 private
49 # Defines a new macro with the given name and block.
49 # Defines a new macro with the given name and block.
50 def macro(name, &block)
50 def macro(name, &block)
51 name = name.to_sym if name.is_a?(String)
51 name = name.to_sym if name.is_a?(String)
52 @@available_macros[name] = @@desc || ''
52 @@available_macros[name] = @@desc || ''
53 @@desc = nil
53 @@desc = nil
54 raise "Can not create a macro without a block!" unless block_given?
54 raise "Can not create a macro without a block!" unless block_given?
55 Definitions.send :define_method, "macro_#{name}".downcase, &block
55 Definitions.send :define_method, "macro_#{name}".downcase, &block
56 end
56 end
57
57
58 # Sets description for the next macro to be defined
58 # Sets description for the next macro to be defined
59 def desc(txt)
59 def desc(txt)
60 @@desc = txt
60 @@desc = txt
61 end
61 end
62 end
62 end
63
63
64 # Builtin macros
64 # Builtin macros
65 desc "Example macro."
65 desc "Sample macro."
66 macro :hello_world do |obj, args|
66 macro :hello_world do |obj, args|
67 "Hello world! Object: #{obj.class.name}, " + (args.empty? ? "Called with no argument." : "Arguments: #{args.join(', ')}")
67 "Hello world! Object: #{obj.class.name}, " + (args.empty? ? "Called with no argument." : "Arguments: #{args.join(', ')}")
68 end
68 end
69
69
70 desc "Displays a list of all available macros, including description if available."
70 desc "Displays a list of all available macros, including description if available."
71 macro :macro_list do
71 macro :macro_list do
72 out = ''
72 out = ''
73 @@available_macros.keys.collect(&:to_s).sort.each do |macro|
73 @@available_macros.keys.collect(&:to_s).sort.each do |macro|
74 out << content_tag('dt', content_tag('code', macro))
74 out << content_tag('dt', content_tag('code', macro))
75 out << content_tag('dd', simple_format(@@available_macros[macro.to_sym]))
75 out << content_tag('dd', textilizable(@@available_macros[macro.to_sym]))
76 end
76 end
77 content_tag('dl', out)
77 content_tag('dl', out)
78 end
78 end
79
80 desc "Include a wiki page. Example:\n\n !{{include(Foo)}}"
81 macro :include do |obj, args|
82 if @project && !@project.wiki.nil?
83 page = @project.wiki.find_page(args.first)
84 if page && page.content
85 @included_wiki_pages ||= []
86 raise 'Circular inclusion detected' if @included_wiki_pages.include?(page.title)
87 @included_wiki_pages << page.title
88 out = textilizable(page.content, :text)
89 @included_wiki_pages.pop
90 out
91 else
92 raise "Page #{args.first} doesn't exist"
93 end
94 end
95 end
79 end
96 end
80 end
97 end
81 end
98 end
@@ -1,152 +1,155
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 ApplicationHelperTest < HelperTestCase
20 class ApplicationHelperTest < HelperTestCase
21 include ApplicationHelper
21 include ApplicationHelper
22 include ActionView::Helpers::TextHelper
22 include ActionView::Helpers::TextHelper
23 fixtures :projects, :repositories, :changesets, :trackers, :issue_statuses, :issues, :documents, :versions, :wikis, :wiki_pages, :wiki_contents
23 fixtures :projects, :repositories, :changesets, :trackers, :issue_statuses, :issues, :documents, :versions, :wikis, :wiki_pages, :wiki_contents
24
24
25 def setup
25 def setup
26 super
26 super
27 end
27 end
28
28
29 def test_auto_links
29 def test_auto_links
30 to_test = {
30 to_test = {
31 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
31 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
32 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
32 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
33 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
33 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
34 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
34 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
35 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
35 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
36 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
36 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
37 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>'
37 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>'
38 }
38 }
39 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
39 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
40 end
40 end
41
41
42 def test_auto_mailto
42 def test_auto_mailto
43 assert_equal '<p><a href="mailto:test@foo.bar" class="email">test@foo.bar</a></p>',
43 assert_equal '<p><a href="mailto:test@foo.bar" class="email">test@foo.bar</a></p>',
44 textilizable('test@foo.bar')
44 textilizable('test@foo.bar')
45 end
45 end
46
46
47 def test_inline_images
47 def test_inline_images
48 to_test = {
48 to_test = {
49 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
49 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
50 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
50 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
51 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
51 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
52 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height100px;" alt="" />',
52 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height100px;" alt="" />',
53 }
53 }
54 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
54 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
55 end
55 end
56
56
57 def test_textile_external_links
57 def test_textile_external_links
58 to_test = {
58 to_test = {
59 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
59 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
60 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
60 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
61 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>'
61 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>'
62 }
62 }
63 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
63 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
64 end
64 end
65
65
66 def test_redmine_links
66 def test_redmine_links
67 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
67 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
68 :class => 'issue', :title => 'Error 281 when updating a recipe (New)')
68 :class => 'issue', :title => 'Error 281 when updating a recipe (New)')
69
69
70 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 1, :rev => 1},
70 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 1, :rev => 1},
71 :class => 'changeset', :title => 'My very first commit')
71 :class => 'changeset', :title => 'My very first commit')
72
72
73 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
73 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
74 :class => 'document')
74 :class => 'document')
75
75
76 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
76 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
77 :class => 'version')
77 :class => 'version')
78
78
79 to_test = {
79 to_test = {
80 '#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.",
80 '#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.",
81 'r1' => changeset_link,
81 'r1' => changeset_link,
82 'document#1' => document_link,
82 'document#1' => document_link,
83 'document:"Test document"' => document_link,
83 'document:"Test document"' => document_link,
84 'version#2' => version_link,
84 'version#2' => version_link,
85 'version:1.0' => version_link,
85 'version:1.0' => version_link,
86 'version:"1.0"' => version_link,
86 'version:"1.0"' => version_link,
87 # escaping
87 # escaping
88 '!#3.' => '#3.',
88 '!#3.' => '#3.',
89 '!r1' => 'r1',
89 '!r1' => 'r1',
90 '!document#1' => 'document#1',
90 '!document#1' => 'document#1',
91 '!document:"Test document"' => 'document:"Test document"',
91 '!document:"Test document"' => 'document:"Test document"',
92 '!version#2' => 'version#2',
92 '!version#2' => 'version#2',
93 '!version:1.0' => 'version:1.0',
93 '!version:1.0' => 'version:1.0',
94 '!version:"1.0"' => 'version:"1.0"',
94 '!version:"1.0"' => 'version:"1.0"',
95 }
95 }
96 @project = Project.find(1)
96 @project = Project.find(1)
97 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
97 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
98 end
98 end
99
99
100 def test_wiki_links
100 def test_wiki_links
101 to_test = {
101 to_test = {
102 '[[CookBook documentation]]' => '<a href="/wiki/ecookbook/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
102 '[[CookBook documentation]]' => '<a href="/wiki/ecookbook/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
103 '[[Another page|Page]]' => '<a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a>',
103 '[[Another page|Page]]' => '<a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a>',
104 # page that doesn't exist
104 # page that doesn't exist
105 '[[Unknown page]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">Unknown page</a>',
105 '[[Unknown page]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">Unknown page</a>',
106 '[[Unknown page|404]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">404</a>',
106 '[[Unknown page|404]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">404</a>',
107 # link to another project wiki
107 # link to another project wiki
108 '[[onlinestore:]]' => '<a href="/wiki/onlinestore/" class="wiki-page">onlinestore</a>',
108 '[[onlinestore:]]' => '<a href="/wiki/onlinestore/" class="wiki-page">onlinestore</a>',
109 '[[onlinestore:|Wiki]]' => '<a href="/wiki/onlinestore/" class="wiki-page">Wiki</a>',
109 '[[onlinestore:|Wiki]]' => '<a href="/wiki/onlinestore/" class="wiki-page">Wiki</a>',
110 '[[onlinestore:Start page]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Start page</a>',
110 '[[onlinestore:Start page]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Start page</a>',
111 '[[onlinestore:Start page|Text]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Text</a>',
111 '[[onlinestore:Start page|Text]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Text</a>',
112 '[[onlinestore:Unknown page]]' => '<a href="/wiki/onlinestore/Unknown_page" class="wiki-page new">Unknown page</a>',
112 '[[onlinestore:Unknown page]]' => '<a href="/wiki/onlinestore/Unknown_page" class="wiki-page new">Unknown page</a>',
113 # escaping
113 # escaping
114 '![[Another page|Page]]' => '[[Another page|Page]]',
114 '![[Another page|Page]]' => '[[Another page|Page]]',
115 }
115 }
116 @project = Project.find(1)
116 @project = Project.find(1)
117 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
117 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
118 end
118 end
119
119
120 def test_macro_hello_world
120 def test_macro_hello_world
121 text = "{{hello_world}}"
121 text = "{{hello_world}}"
122 assert textilizable(text).match(/Hello world!/)
122 assert textilizable(text).match(/Hello world!/)
123 # escaping
124 text = "!{{hello_world}}"
125 assert_equal '<p>{{hello_world}}</p>', textilizable(text)
123 end
126 end
124
127
125 def test_date_format_default
128 def test_date_format_default
126 today = Date.today
129 today = Date.today
127 Setting.date_format = ''
130 Setting.date_format = ''
128 assert_equal l_date(today), format_date(today)
131 assert_equal l_date(today), format_date(today)
129 end
132 end
130
133
131 def test_date_format
134 def test_date_format
132 today = Date.today
135 today = Date.today
133 Setting.date_format = '%d %m %Y'
136 Setting.date_format = '%d %m %Y'
134 assert_equal today.strftime('%d %m %Y'), format_date(today)
137 assert_equal today.strftime('%d %m %Y'), format_date(today)
135 end
138 end
136
139
137 def test_time_format_default
140 def test_time_format_default
138 now = Time.now
141 now = Time.now
139 Setting.date_format = ''
142 Setting.date_format = ''
140 Setting.time_format = ''
143 Setting.time_format = ''
141 assert_equal l_datetime(now), format_time(now)
144 assert_equal l_datetime(now), format_time(now)
142 assert_equal l_time(now), format_time(now, false)
145 assert_equal l_time(now), format_time(now, false)
143 end
146 end
144
147
145 def test_time_format
148 def test_time_format
146 now = Time.now
149 now = Time.now
147 Setting.date_format = '%d %m %Y'
150 Setting.date_format = '%d %m %Y'
148 Setting.time_format = '%H %M'
151 Setting.time_format = '%H %M'
149 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now)
152 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now)
150 assert_equal now.strftime('%H %M'), format_time(now, false)
153 assert_equal now.strftime('%H %M'), format_time(now, false)
151 end
154 end
152 end
155 end
General Comments 0
You need to be logged in to leave comments. Login now