##// END OF EJS Templates
Turn ftp urls into links (#1514)....
Jean-Philippe Lang -
r1563:28c094f50e3c
parent child
Show More
@@ -1,175 +1,176
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'redcloth'
19 19 require 'coderay'
20 20
21 21 module Redmine
22 22 module WikiFormatting
23 23
24 24 private
25 25
26 26 class TextileFormatter < RedCloth
27 27
28 28 # auto_link rule after textile rules so that it doesn't break !image_url! tags
29 29 RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
30 30
31 31 def initialize(*args)
32 32 super
33 33 self.hard_breaks=true
34 34 self.no_span_caps=true
35 35 end
36 36
37 37 def to_html(*rules, &block)
38 38 @toc = []
39 39 @macros_runner = block
40 40 super(*RULES).to_s
41 41 end
42 42
43 43 private
44 44
45 45 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
46 46 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
47 47 def hard_break( text )
48 48 text.gsub!( /(.)\n(?!\n|\Z|>| *(>? *[#*=]+(\s|$)|[{|]))/, "\\1<br />\n" ) if hard_breaks
49 49 end
50 50
51 51 # Patch to add code highlighting support to RedCloth
52 52 def smooth_offtags( text )
53 53 unless @pre_list.empty?
54 54 ## replace <pre> content
55 55 text.gsub!(/<redpre#(\d+)>/) do
56 56 content = @pre_list[$1.to_i]
57 57 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
58 58 content = "<code class=\"#{$1} CodeRay\">" +
59 59 CodeRay.scan($2, $1.downcase).html(:escape => false, :line_numbers => :inline)
60 60 end
61 61 content
62 62 end
63 63 end
64 64 end
65 65
66 66 # Patch to add 'table of content' support to RedCloth
67 67 def textile_p_withtoc(tag, atts, cite, content)
68 68 if tag =~ /^h(\d)$/
69 69 @toc << [$1.to_i, content]
70 70 end
71 71 content = "<a name=\"#{@toc.length}\" class=\"wiki-page\"></a>" + content
72 72 textile_p(tag, atts, cite, content)
73 73 end
74 74
75 75 alias :textile_h1 :textile_p_withtoc
76 76 alias :textile_h2 :textile_p_withtoc
77 77 alias :textile_h3 :textile_p_withtoc
78 78
79 79 def inline_toc(text)
80 80 text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
81 81 div_class = 'toc'
82 82 div_class << ' right' if $1 == '>'
83 83 div_class << ' left' if $1 == '<'
84 84 out = "<div class=\"#{div_class}\">"
85 85 @toc.each_with_index do |heading, index|
86 86 # remove wiki links from the item
87 87 toc_item = heading.last.gsub(/(\[\[|\]\])/, '')
88 88 # remove styles
89 89 # eg. %{color:red}Triggers% => Triggers
90 90 toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1'
91 91 out << "<a href=\"##{index+1}\" class=\"heading#{heading.first}\">#{toc_item}</a>"
92 92 end
93 93 out << '</div>'
94 94 out
95 95 end
96 96 end
97 97
98 98 MACROS_RE = /
99 99 (!)? # escaping
100 100 (
101 101 \{\{ # opening tag
102 102 ([\w]+) # macro name
103 103 (\(([^\}]*)\))? # optional arguments
104 104 \}\} # closing tag
105 105 )
106 106 /x unless const_defined?(:MACROS_RE)
107 107
108 108 def inline_macros(text)
109 109 text.gsub!(MACROS_RE) do
110 110 esc, all, macro = $1, $2, $3.downcase
111 111 args = ($5 || '').split(',').each(&:strip)
112 112 if esc.nil?
113 113 begin
114 114 @macros_runner.call(macro, args)
115 115 rescue => e
116 116 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
117 117 end || all
118 118 else
119 119 all
120 120 end
121 121 end
122 122 end
123 123
124 124 AUTO_LINK_RE = %r{
125 125 ( # leading text
126 126 <\w+.*?>| # leading HTML tag, or
127 127 [^=<>!:'"/]| # leading punctuation, or
128 128 ^ # beginning of line
129 129 )
130 130 (
131 131 (?:https?://)| # protocol spec, or
132 (?:ftp://)|
132 133 (?:www\.) # www.*
133 134 )
134 135 (
135 136 (\S+?) # url
136 137 (\/)? # slash
137 138 )
138 139 ([^\w\=\/;]*?) # post
139 140 (?=<|\s|$)
140 141 }x unless const_defined?(:AUTO_LINK_RE)
141 142
142 143 # Turns all urls into clickable links (code from Rails).
143 144 def inline_auto_link(text)
144 145 text.gsub!(AUTO_LINK_RE) do
145 146 all, leading, proto, url, post = $&, $1, $2, $3, $6
146 147 if leading =~ /<a\s/i || leading =~ /![<>=]?/
147 148 # don't replace URL's that are already linked
148 149 # and URL's prefixed with ! !> !< != (textile images)
149 150 all
150 151 else
151 152 %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
152 153 end
153 154 end
154 155 end
155 156
156 157 # Turns all email addresses into clickable links (code from Rails).
157 158 def inline_auto_mailto(text)
158 159 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
159 160 mail = $1
160 161 if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/)
161 162 mail
162 163 else
163 164 %{<a href="mailto:#{mail}" class="email">#{mail}</a>}
164 165 end
165 166 end
166 167 end
167 168 end
168 169
169 170 public
170 171
171 172 def self.to_html(text, options = {}, &block)
172 173 TextileFormatter.new(text).to_html(&block)
173 174 end
174 175 end
175 176 end
@@ -1,331 +1,332
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../../test_helper'
19 19
20 20 class ApplicationHelperTest < HelperTestCase
21 21 include ApplicationHelper
22 22 include ActionView::Helpers::TextHelper
23 23 fixtures :projects, :repositories, :changesets, :trackers, :issue_statuses, :issues, :documents, :versions, :wikis, :wiki_pages, :wiki_contents, :roles, :enabled_modules
24 24
25 25 def setup
26 26 super
27 27 end
28 28
29 29 def test_auto_links
30 30 to_test = {
31 31 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
32 32 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
33 33 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
34 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 35 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
36 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 37 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
38 38 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
39 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
39 40 }
40 41 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
41 42 end
42 43
43 44 def test_auto_mailto
44 45 assert_equal '<p><a href="mailto:test@foo.bar" class="email">test@foo.bar</a></p>',
45 46 textilizable('test@foo.bar')
46 47 end
47 48
48 49 def test_inline_images
49 50 to_test = {
50 51 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
51 52 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
52 53 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
53 54 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height100px;" alt="" />',
54 55 }
55 56 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
56 57 end
57 58
58 59 def test_textile_external_links
59 60 to_test = {
60 61 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
61 62 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
62 63 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
63 64 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
64 65 # no multiline link text
65 66 "This is a double quote \"on the first line\nand another on a second line\":test" => "This is a double quote \"on the first line<br />\nand another on a second line\":test"
66 67 }
67 68 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
68 69 end
69 70
70 71 def test_redmine_links
71 72 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
72 73 :class => 'issue', :title => 'Error 281 when updating a recipe (New)')
73 74
74 75 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
75 76 :class => 'changeset', :title => 'My very first commit')
76 77
77 78 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
78 79 :class => 'document')
79 80
80 81 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
81 82 :class => 'version')
82 83
83 84 source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => 'some/file'}
84 85
85 86 to_test = {
86 87 # tickets
87 88 '#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.",
88 89 # changesets
89 90 'r1' => changeset_link,
90 91 # documents
91 92 'document#1' => document_link,
92 93 'document:"Test document"' => document_link,
93 94 # versions
94 95 'version#2' => version_link,
95 96 'version:1.0' => version_link,
96 97 'version:"1.0"' => version_link,
97 98 # source
98 99 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
99 100 'source:/some/file@52' => link_to('source:/some/file@52', source_url.merge(:rev => 52), :class => 'source'),
100 101 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
101 102 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
102 103 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
103 104 # escaping
104 105 '!#3.' => '#3.',
105 106 '!r1' => 'r1',
106 107 '!document#1' => 'document#1',
107 108 '!document:"Test document"' => 'document:"Test document"',
108 109 '!version#2' => 'version#2',
109 110 '!version:1.0' => 'version:1.0',
110 111 '!version:"1.0"' => 'version:"1.0"',
111 112 '!source:/some/file' => 'source:/some/file',
112 113 # invalid expressions
113 114 'source:' => 'source:',
114 115 # url hash
115 116 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
116 117 }
117 118 @project = Project.find(1)
118 119 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
119 120 end
120 121
121 122 def test_wiki_links
122 123 to_test = {
123 124 '[[CookBook documentation]]' => '<a href="/wiki/ecookbook/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
124 125 '[[Another page|Page]]' => '<a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a>',
125 126 # page that doesn't exist
126 127 '[[Unknown page]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">Unknown page</a>',
127 128 '[[Unknown page|404]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">404</a>',
128 129 # link to another project wiki
129 130 '[[onlinestore:]]' => '<a href="/wiki/onlinestore/" class="wiki-page">onlinestore</a>',
130 131 '[[onlinestore:|Wiki]]' => '<a href="/wiki/onlinestore/" class="wiki-page">Wiki</a>',
131 132 '[[onlinestore:Start page]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Start page</a>',
132 133 '[[onlinestore:Start page|Text]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Text</a>',
133 134 '[[onlinestore:Unknown page]]' => '<a href="/wiki/onlinestore/Unknown_page" class="wiki-page new">Unknown page</a>',
134 135 # striked through link
135 136 '-[[Another page|Page]]-' => '<del><a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a></del>',
136 137 # escaping
137 138 '![[Another page|Page]]' => '[[Another page|Page]]',
138 139 }
139 140 @project = Project.find(1)
140 141 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
141 142 end
142 143
143 144 def test_html_tags
144 145 to_test = {
145 146 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
146 147 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
147 148 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
148 149 # do not escape pre/code tags
149 150 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
150 151 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
151 152 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
152 153 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
153 154 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>"
154 155 }
155 156 to_test.each { |text, result| assert_equal result, textilizable(text) }
156 157 end
157 158
158 159 def test_allowed_html_tags
159 160 to_test = {
160 161 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
161 162 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
162 163 }
163 164 to_test.each { |text, result| assert_equal result, textilizable(text) }
164 165 end
165 166
166 167 def test_wiki_links_in_tables
167 168 to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
168 169 '<tr><td><a href="/wiki/ecookbook/Page" class="wiki-page new">Link title</a></td>' +
169 170 '<td><a href="/wiki/ecookbook/Other_Page" class="wiki-page new">Other title</a></td>' +
170 171 '</tr><tr><td>Cell 21</td><td><a href="/wiki/ecookbook/Last_page" class="wiki-page new">Last page</a></td></tr>'
171 172 }
172 173 @project = Project.find(1)
173 174 to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
174 175 end
175 176
176 177 def test_text_formatting
177 178 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
178 179 '(_text within parentheses_)' => '(<em>text within parentheses</em>)'
179 180 }
180 181 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
181 182 end
182 183
183 184 def test_wiki_horizontal_rule
184 185 assert_equal '<hr />', textilizable('---')
185 186 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
186 187 end
187 188
188 189 def test_table_of_content
189 190 raw = <<-RAW
190 191 {{toc}}
191 192
192 193 h1. Title
193 194
194 195 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
195 196
196 197 h2. Subtitle
197 198
198 199 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
199 200
200 201 h2. Subtitle with %{color:red}red text%
201 202
202 203 h1. Another title
203 204
204 205 RAW
205 206
206 207 expected = '<div class="toc">' +
207 208 '<a href="#1" class="heading1">Title</a>' +
208 209 '<a href="#2" class="heading2">Subtitle</a>' +
209 210 '<a href="#3" class="heading2">Subtitle with red text</a>' +
210 211 '<a href="#4" class="heading1">Another title</a>' +
211 212 '</div>'
212 213
213 214 assert textilizable(raw).include?(expected)
214 215 end
215 216
216 217 def test_blockquote
217 218 # orig raw text
218 219 raw = <<-RAW
219 220 John said:
220 221 > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
221 222 > Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
222 223 > * Donec odio lorem,
223 224 > * sagittis ac,
224 225 > * malesuada in,
225 226 > * adipiscing eu, dolor.
226 227 >
227 228 > >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
228 229 > Proin a tellus. Nam vel neque.
229 230
230 231 He's right.
231 232 RAW
232 233
233 234 # expected html
234 235 expected = <<-EXPECTED
235 236 <p>John said:</p>
236 237 <blockquote>
237 238 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
238 239 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
239 240 <ul>
240 241 <li>Donec odio lorem,</li>
241 242 <li>sagittis ac,</li>
242 243 <li>malesuada in,</li>
243 244 <li>adipiscing eu, dolor.</li>
244 245 </ul>
245 246 <blockquote>
246 247 <p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
247 248 </blockquote>
248 249 <p>Proin a tellus. Nam vel neque.</p>
249 250 </blockquote>
250 251 <p>He's right.</p>
251 252 EXPECTED
252 253
253 254 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
254 255 end
255 256
256 257 def test_table
257 258 raw = <<-RAW
258 259 This is a table with empty cells:
259 260
260 261 |cell11|cell12||
261 262 |cell21||cell23|
262 263 |cell31|cell32|cell33|
263 264 RAW
264 265
265 266 expected = <<-EXPECTED
266 267 <p>This is a table with empty cells:</p>
267 268
268 269 <table>
269 270 <tr><td>cell11</td><td>cell12</td><td></td></tr>
270 271 <tr><td>cell21</td><td></td><td>cell23</td></tr>
271 272 <tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
272 273 </table>
273 274 EXPECTED
274 275
275 276 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
276 277 end
277 278
278 279 def test_macro_hello_world
279 280 text = "{{hello_world}}"
280 281 assert textilizable(text).match(/Hello world!/)
281 282 # escaping
282 283 text = "!{{hello_world}}"
283 284 assert_equal '<p>{{hello_world}}</p>', textilizable(text)
284 285 end
285 286
286 287 def test_macro_include
287 288 @project = Project.find(1)
288 289 # include a page of the current project wiki
289 290 text = "{{include(Another page)}}"
290 291 assert textilizable(text).match(/This is a link to a ticket/)
291 292
292 293 @project = nil
293 294 # include a page of a specific project wiki
294 295 text = "{{include(ecookbook:Another page)}}"
295 296 assert textilizable(text).match(/This is a link to a ticket/)
296 297
297 298 text = "{{include(ecookbook:)}}"
298 299 assert textilizable(text).match(/CookBook documentation/)
299 300
300 301 text = "{{include(unknowidentifier:somepage)}}"
301 302 assert textilizable(text).match(/Unknow project/)
302 303 end
303 304
304 305 def test_date_format_default
305 306 today = Date.today
306 307 Setting.date_format = ''
307 308 assert_equal l_date(today), format_date(today)
308 309 end
309 310
310 311 def test_date_format
311 312 today = Date.today
312 313 Setting.date_format = '%d %m %Y'
313 314 assert_equal today.strftime('%d %m %Y'), format_date(today)
314 315 end
315 316
316 317 def test_time_format_default
317 318 now = Time.now
318 319 Setting.date_format = ''
319 320 Setting.time_format = ''
320 321 assert_equal l_datetime(now), format_time(now)
321 322 assert_equal l_time(now), format_time(now, false)
322 323 end
323 324
324 325 def test_time_format
325 326 now = Time.now
326 327 Setting.date_format = '%d %m %Y'
327 328 Setting.time_format = '%H %M'
328 329 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now)
329 330 assert_equal now.strftime('%H %M'), format_time(now, false)
330 331 end
331 332 end
General Comments 0
You need to be logged in to leave comments. Login now