##// END OF EJS Templates
Fixed: Inline images don't work if file name has upper case letters or if image is in BMP format (#2102)....
Jean-Philippe Lang -
r1956:0f1787ea0dfa
parent child
Show More
@@ -1,571 +1,571
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 'coderay'
19 19 require 'coderay/helpers/file_type'
20 20 require 'forwardable'
21 21
22 22 module ApplicationHelper
23 23 include Redmine::WikiFormatting::Macros::Definitions
24 24
25 25 extend Forwardable
26 26 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
27 27
28 28 def current_role
29 29 @current_role ||= User.current.role_for_project(@project)
30 30 end
31 31
32 32 # Return true if user is authorized for controller/action, otherwise false
33 33 def authorize_for(controller, action)
34 34 User.current.allowed_to?({:controller => controller, :action => action}, @project)
35 35 end
36 36
37 37 # Display a link if user is authorized
38 38 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
39 39 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
40 40 end
41 41
42 42 # Display a link to remote if user is authorized
43 43 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
44 44 url = options[:url] || {}
45 45 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
46 46 end
47 47
48 48 # Display a link to user's account page
49 49 def link_to_user(user)
50 50 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
51 51 end
52 52
53 53 def link_to_issue(issue, options={})
54 54 options[:class] ||= ''
55 55 options[:class] << ' issue'
56 56 options[:class] << ' closed' if issue.closed?
57 57 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
58 58 end
59 59
60 60 # Generates a link to an attachment.
61 61 # Options:
62 62 # * :text - Link text (default to attachment filename)
63 63 # * :download - Force download (default: false)
64 64 def link_to_attachment(attachment, options={})
65 65 text = options.delete(:text) || attachment.filename
66 66 action = options.delete(:download) ? 'download' : 'show'
67 67
68 68 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
69 69 end
70 70
71 71 def toggle_link(name, id, options={})
72 72 onclick = "Element.toggle('#{id}'); "
73 73 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
74 74 onclick << "return false;"
75 75 link_to(name, "#", :onclick => onclick)
76 76 end
77 77
78 78 def image_to_function(name, function, html_options = {})
79 79 html_options.symbolize_keys!
80 80 tag(:input, html_options.merge({
81 81 :type => "image", :src => image_path(name),
82 82 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
83 83 }))
84 84 end
85 85
86 86 def prompt_to_remote(name, text, param, url, html_options = {})
87 87 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
88 88 link_to name, {}, html_options
89 89 end
90 90
91 91 def format_date(date)
92 92 return nil unless date
93 93 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
94 94 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
95 95 date.strftime(@date_format)
96 96 end
97 97
98 98 def format_time(time, include_date = true)
99 99 return nil unless time
100 100 time = time.to_time if time.is_a?(String)
101 101 zone = User.current.time_zone
102 102 local = zone ? time.in_time_zone(zone) : (time.utc? ? time.localtime : time)
103 103 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
104 104 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
105 105 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
106 106 end
107 107
108 108 def distance_of_date_in_words(from_date, to_date = 0)
109 109 from_date = from_date.to_date if from_date.respond_to?(:to_date)
110 110 to_date = to_date.to_date if to_date.respond_to?(:to_date)
111 111 distance_in_days = (to_date - from_date).abs
112 112 lwr(:actionview_datehelper_time_in_words_day, distance_in_days)
113 113 end
114 114
115 115 def due_date_distance_in_words(date)
116 116 if date
117 117 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
118 118 end
119 119 end
120 120
121 121 # Truncates and returns the string as a single line
122 122 def truncate_single_line(string, *args)
123 123 truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
124 124 end
125 125
126 126 def html_hours(text)
127 127 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
128 128 end
129 129
130 130 def authoring(created, author)
131 131 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
132 132 author_tag = (author.is_a?(User) && !author.anonymous?) ? link_to(h(author), :controller => 'account', :action => 'show', :id => author) : h(author || 'Anonymous')
133 133 l(:label_added_time_by, author_tag, time_tag)
134 134 end
135 135
136 136 def l_or_humanize(s, options={})
137 137 k = "#{options[:prefix]}#{s}".to_sym
138 138 l_has_string?(k) ? l(k) : s.to_s.humanize
139 139 end
140 140
141 141 def day_name(day)
142 142 l(:general_day_names).split(',')[day-1]
143 143 end
144 144
145 145 def month_name(month)
146 146 l(:actionview_datehelper_select_month_names).split(',')[month-1]
147 147 end
148 148
149 149 def syntax_highlight(name, content)
150 150 type = CodeRay::FileType[name]
151 151 type ? CodeRay.scan(content, type).html : h(content)
152 152 end
153 153
154 154 def to_path_param(path)
155 155 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
156 156 end
157 157
158 158 def pagination_links_full(paginator, count=nil, options={})
159 159 page_param = options.delete(:page_param) || :page
160 160 url_param = params.dup
161 161 # don't reuse params if filters are present
162 162 url_param.clear if url_param.has_key?(:set_filter)
163 163
164 164 html = ''
165 165 html << link_to_remote(('&#171; ' + l(:label_previous)),
166 166 {:update => 'content',
167 167 :url => url_param.merge(page_param => paginator.current.previous),
168 168 :complete => 'window.scrollTo(0,0)'},
169 169 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
170 170
171 171 html << (pagination_links_each(paginator, options) do |n|
172 172 link_to_remote(n.to_s,
173 173 {:url => {:params => url_param.merge(page_param => n)},
174 174 :update => 'content',
175 175 :complete => 'window.scrollTo(0,0)'},
176 176 {:href => url_for(:params => url_param.merge(page_param => n))})
177 177 end || '')
178 178
179 179 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
180 180 {:update => 'content',
181 181 :url => url_param.merge(page_param => paginator.current.next),
182 182 :complete => 'window.scrollTo(0,0)'},
183 183 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
184 184
185 185 unless count.nil?
186 186 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
187 187 end
188 188
189 189 html
190 190 end
191 191
192 192 def per_page_links(selected=nil)
193 193 url_param = params.dup
194 194 url_param.clear if url_param.has_key?(:set_filter)
195 195
196 196 links = Setting.per_page_options_array.collect do |n|
197 197 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
198 198 {:href => url_for(url_param.merge(:per_page => n))})
199 199 end
200 200 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
201 201 end
202 202
203 203 def breadcrumb(*args)
204 204 elements = args.flatten
205 205 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
206 206 end
207 207
208 208 def html_title(*args)
209 209 if args.empty?
210 210 title = []
211 211 title << @project.name if @project
212 212 title += @html_title if @html_title
213 213 title << Setting.app_title
214 214 title.compact.join(' - ')
215 215 else
216 216 @html_title ||= []
217 217 @html_title += args
218 218 end
219 219 end
220 220
221 221 def accesskey(s)
222 222 Redmine::AccessKeys.key_for s
223 223 end
224 224
225 225 # Formats text according to system settings.
226 226 # 2 ways to call this method:
227 227 # * with a String: textilizable(text, options)
228 228 # * with an object and one of its attribute: textilizable(issue, :description, options)
229 229 def textilizable(*args)
230 230 options = args.last.is_a?(Hash) ? args.pop : {}
231 231 case args.size
232 232 when 1
233 233 obj = options[:object]
234 234 text = args.shift
235 235 when 2
236 236 obj = args.shift
237 237 text = obj.send(args.shift).to_s
238 238 else
239 239 raise ArgumentError, 'invalid arguments to textilizable'
240 240 end
241 241 return '' if text.blank?
242 242
243 243 only_path = options.delete(:only_path) == false ? false : true
244 244
245 245 # when using an image link, try to use an attachment, if possible
246 246 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
247 247
248 248 if attachments
249 249 attachments = attachments.sort_by(&:created_on).reverse
250 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
250 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
251 251 style = $1
252 252 filename = $6
253 253 rf = Regexp.new(Regexp.escape(filename), Regexp::IGNORECASE)
254 254 # search for the picture in attachments
255 255 if found = attachments.detect { |att| att.filename =~ rf }
256 256 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
257 257 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
258 258 alt = desc.blank? ? nil : "(#{desc})"
259 259 "!#{style}#{image_url}#{alt}!"
260 260 else
261 261 "!#{style}#{filename}!"
262 262 end
263 263 end
264 264 end
265 265
266 266 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
267 267
268 268 # different methods for formatting wiki links
269 269 case options[:wiki_links]
270 270 when :local
271 271 # used for local links to html files
272 272 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
273 273 when :anchor
274 274 # used for single-file wiki export
275 275 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
276 276 else
277 277 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
278 278 end
279 279
280 280 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
281 281
282 282 # Wiki links
283 283 #
284 284 # Examples:
285 285 # [[mypage]]
286 286 # [[mypage|mytext]]
287 287 # wiki links can refer other project wikis, using project name or identifier:
288 288 # [[project:]] -> wiki starting page
289 289 # [[project:|mytext]]
290 290 # [[project:mypage]]
291 291 # [[project:mypage|mytext]]
292 292 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
293 293 link_project = project
294 294 esc, all, page, title = $1, $2, $3, $5
295 295 if esc.nil?
296 296 if page =~ /^([^\:]+)\:(.*)$/
297 297 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
298 298 page = $2
299 299 title ||= $1 if page.blank?
300 300 end
301 301
302 302 if link_project && link_project.wiki
303 303 # extract anchor
304 304 anchor = nil
305 305 if page =~ /^(.+?)\#(.+)$/
306 306 page, anchor = $1, $2
307 307 end
308 308 # check if page exists
309 309 wiki_page = link_project.wiki.find_page(page)
310 310 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
311 311 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
312 312 else
313 313 # project or wiki doesn't exist
314 314 title || page
315 315 end
316 316 else
317 317 all
318 318 end
319 319 end
320 320
321 321 # Redmine links
322 322 #
323 323 # Examples:
324 324 # Issues:
325 325 # #52 -> Link to issue #52
326 326 # Changesets:
327 327 # r52 -> Link to revision 52
328 328 # commit:a85130f -> Link to scmid starting with a85130f
329 329 # Documents:
330 330 # document#17 -> Link to document with id 17
331 331 # document:Greetings -> Link to the document with title "Greetings"
332 332 # document:"Some document" -> Link to the document with title "Some document"
333 333 # Versions:
334 334 # version#3 -> Link to version with id 3
335 335 # version:1.0.0 -> Link to version named "1.0.0"
336 336 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
337 337 # Attachments:
338 338 # attachment:file.zip -> Link to the attachment of the current object named file.zip
339 339 # Source files:
340 340 # source:some/file -> Link to the file located at /some/file in the project's repository
341 341 # source:some/file@52 -> Link to the file's revision 52
342 342 # source:some/file#L120 -> Link to line 120 of the file
343 343 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
344 344 # export:some/file -> Force the download of the file
345 345 # Forum messages:
346 346 # message#1218 -> Link to message with id 1218
347 347 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|\s|<|$)}) do |m|
348 348 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
349 349 link = nil
350 350 if esc.nil?
351 351 if prefix.nil? && sep == 'r'
352 352 if project && (changeset = project.changesets.find_by_revision(oid))
353 353 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
354 354 :class => 'changeset',
355 355 :title => truncate_single_line(changeset.comments, 100))
356 356 end
357 357 elsif sep == '#'
358 358 oid = oid.to_i
359 359 case prefix
360 360 when nil
361 361 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
362 362 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
363 363 :class => (issue.closed? ? 'issue closed' : 'issue'),
364 364 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
365 365 link = content_tag('del', link) if issue.closed?
366 366 end
367 367 when 'document'
368 368 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
369 369 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
370 370 :class => 'document'
371 371 end
372 372 when 'version'
373 373 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
374 374 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
375 375 :class => 'version'
376 376 end
377 377 when 'message'
378 378 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
379 379 link = link_to h(truncate(message.subject, 60)), {:only_path => only_path,
380 380 :controller => 'messages',
381 381 :action => 'show',
382 382 :board_id => message.board,
383 383 :id => message.root,
384 384 :anchor => (message.parent ? "message-#{message.id}" : nil)},
385 385 :class => 'message'
386 386 end
387 387 end
388 388 elsif sep == ':'
389 389 # removes the double quotes if any
390 390 name = oid.gsub(%r{^"(.*)"$}, "\\1")
391 391 case prefix
392 392 when 'document'
393 393 if project && document = project.documents.find_by_title(name)
394 394 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
395 395 :class => 'document'
396 396 end
397 397 when 'version'
398 398 if project && version = project.versions.find_by_name(name)
399 399 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
400 400 :class => 'version'
401 401 end
402 402 when 'commit'
403 403 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
404 404 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
405 405 :class => 'changeset',
406 406 :title => truncate_single_line(changeset.comments, 100)
407 407 end
408 408 when 'source', 'export'
409 409 if project && project.repository
410 410 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
411 411 path, rev, anchor = $1, $3, $5
412 412 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
413 413 :path => to_path_param(path),
414 414 :rev => rev,
415 415 :anchor => anchor,
416 416 :format => (prefix == 'export' ? 'raw' : nil)},
417 417 :class => (prefix == 'export' ? 'source download' : 'source')
418 418 end
419 419 when 'attachment'
420 420 if attachments && attachment = attachments.detect {|a| a.filename == name }
421 421 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
422 422 :class => 'attachment'
423 423 end
424 424 end
425 425 end
426 426 end
427 427 leading + (link || "#{prefix}#{sep}#{oid}")
428 428 end
429 429
430 430 text
431 431 end
432 432
433 433 # Same as Rails' simple_format helper without using paragraphs
434 434 def simple_format_without_paragraph(text)
435 435 text.to_s.
436 436 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
437 437 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
438 438 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
439 439 end
440 440
441 441 def error_messages_for(object_name, options = {})
442 442 options = options.symbolize_keys
443 443 object = instance_variable_get("@#{object_name}")
444 444 if object && !object.errors.empty?
445 445 # build full_messages here with controller current language
446 446 full_messages = []
447 447 object.errors.each do |attr, msg|
448 448 next if msg.nil?
449 449 msg = msg.first if msg.is_a? Array
450 450 if attr == "base"
451 451 full_messages << l(msg)
452 452 else
453 453 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
454 454 end
455 455 end
456 456 # retrieve custom values error messages
457 457 if object.errors[:custom_values]
458 458 object.custom_values.each do |v|
459 459 v.errors.each do |attr, msg|
460 460 next if msg.nil?
461 461 msg = msg.first if msg.is_a? Array
462 462 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
463 463 end
464 464 end
465 465 end
466 466 content_tag("div",
467 467 content_tag(
468 468 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
469 469 ) +
470 470 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
471 471 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
472 472 )
473 473 else
474 474 ""
475 475 end
476 476 end
477 477
478 478 def lang_options_for_select(blank=true)
479 479 (blank ? [["(auto)", ""]] : []) +
480 480 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
481 481 end
482 482
483 483 def label_tag_for(name, option_tags = nil, options = {})
484 484 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
485 485 content_tag("label", label_text)
486 486 end
487 487
488 488 def labelled_tabular_form_for(name, object, options, &proc)
489 489 options[:html] ||= {}
490 490 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
491 491 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
492 492 end
493 493
494 494 def back_url_hidden_field_tag
495 495 back_url = params[:back_url] || request.env['HTTP_REFERER']
496 496 hidden_field_tag('back_url', back_url) unless back_url.blank?
497 497 end
498 498
499 499 def check_all_links(form_name)
500 500 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
501 501 " | " +
502 502 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
503 503 end
504 504
505 505 def progress_bar(pcts, options={})
506 506 pcts = [pcts, pcts] unless pcts.is_a?(Array)
507 507 pcts[1] = pcts[1] - pcts[0]
508 508 pcts << (100 - pcts[1] - pcts[0])
509 509 width = options[:width] || '100px;'
510 510 legend = options[:legend] || ''
511 511 content_tag('table',
512 512 content_tag('tr',
513 513 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
514 514 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
515 515 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
516 516 ), :class => 'progress', :style => "width: #{width};") +
517 517 content_tag('p', legend, :class => 'pourcent')
518 518 end
519 519
520 520 def context_menu_link(name, url, options={})
521 521 options[:class] ||= ''
522 522 if options.delete(:selected)
523 523 options[:class] << ' icon-checked disabled'
524 524 options[:disabled] = true
525 525 end
526 526 if options.delete(:disabled)
527 527 options.delete(:method)
528 528 options.delete(:confirm)
529 529 options.delete(:onclick)
530 530 options[:class] << ' disabled'
531 531 url = '#'
532 532 end
533 533 link_to name, url, options
534 534 end
535 535
536 536 def calendar_for(field_id)
537 537 include_calendar_headers_tags
538 538 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
539 539 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
540 540 end
541 541
542 542 def include_calendar_headers_tags
543 543 unless @calendar_headers_tags_included
544 544 @calendar_headers_tags_included = true
545 545 content_for :header_tags do
546 546 javascript_include_tag('calendar/calendar') +
547 547 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
548 548 javascript_include_tag('calendar/calendar-setup') +
549 549 stylesheet_link_tag('calendar')
550 550 end
551 551 end
552 552 end
553 553
554 554 def content_for(name, content = nil, &block)
555 555 @has_content ||= {}
556 556 @has_content[name] = true
557 557 super(name, content, &block)
558 558 end
559 559
560 560 def has_content?(name)
561 561 (@has_content && @has_content[name]) || false
562 562 end
563 563
564 564 private
565 565
566 566 def wiki_helper
567 567 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
568 568 extend helper
569 569 return self
570 570 end
571 571 end
@@ -1,414 +1,424
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, :roles, :enabled_modules,
24 24 :repositories, :changesets,
25 25 :trackers, :issue_statuses, :issues, :versions, :documents,
26 26 :wikis, :wiki_pages, :wiki_contents,
27 :boards, :messages
27 :boards, :messages,
28 :attachments
28 29
29 30 def setup
30 31 super
31 32 end
32 33
33 34 def test_auto_links
34 35 to_test = {
35 36 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
36 37 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
37 38 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
38 39 'This is a link: http://foo.bar.' => 'This is a link: <a class="external" href="http://foo.bar">http://foo.bar</a>.',
39 40 'A link (eg. http://foo.bar).' => 'A link (eg. <a class="external" href="http://foo.bar">http://foo.bar</a>).',
40 41 '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>.',
41 42 'http://www.foo.bar/Test_(foobar)' => '<a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>',
42 43 '(see inline link : http://www.foo.bar/Test_(foobar))' => '(see inline link : <a class="external" href="http://www.foo.bar/Test_(foobar)">http://www.foo.bar/Test_(foobar)</a>)',
43 44 '(see inline link : http://www.foo.bar/Test)' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>)',
44 45 '(see inline link : http://www.foo.bar/Test).' => '(see inline link : <a class="external" href="http://www.foo.bar/Test">http://www.foo.bar/Test</a>).',
45 46 '(see "inline link":http://www.foo.bar/Test_(foobar))' => '(see <a href="http://www.foo.bar/Test_(foobar)" class="external">inline link</a>)',
46 47 '(see "inline link":http://www.foo.bar/Test)' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>)',
47 48 '(see "inline link":http://www.foo.bar/Test).' => '(see <a href="http://www.foo.bar/Test" class="external">inline link</a>).',
48 49 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
49 50 '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>',
50 51 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>',
51 52 'http://foo@www.bar.com' => '<a class="external" href="http://foo@www.bar.com">http://foo@www.bar.com</a>',
52 53 'http://foo:bar@www.bar.com' => '<a class="external" href="http://foo:bar@www.bar.com">http://foo:bar@www.bar.com</a>',
53 54 'ftp://foo.bar' => '<a class="external" href="ftp://foo.bar">ftp://foo.bar</a>',
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_auto_mailto
59 60 assert_equal '<p><a href="mailto:test@foo.bar" class="email">test@foo.bar</a></p>',
60 61 textilizable('test@foo.bar')
61 62 end
62 63
63 64 def test_inline_images
64 65 to_test = {
65 66 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
66 67 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
67 68 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
68 69 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height100px;" alt="" />',
69 70 }
70 71 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
71 72 end
72 73
74 def test_attached_images
75 to_test = {
76 'Inline image: !logo.gif!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />',
77 'Inline image: !logo.GIF!' => 'Inline image: <img src="/attachments/download/3" title="This is a logo" alt="This is a logo" />'
78 }
79 attachments = Attachment.find(:all)
80 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
81 end
82
73 83 def test_textile_external_links
74 84 to_test = {
75 85 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
76 86 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
77 87 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
78 88 "This is not a \"Link\":\n\nAnother paragraph" => "This is not a \"Link\":</p>\n\n\n\t<p>Another paragraph",
79 89 # no multiline link text
80 90 "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"
81 91 }
82 92 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
83 93 end
84 94
85 95 def test_redmine_links
86 96 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
87 97 :class => 'issue', :title => 'Error 281 when updating a recipe (New)')
88 98
89 99 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 1},
90 100 :class => 'changeset', :title => 'My very first commit')
91 101
92 102 document_link = link_to('Test document', {:controller => 'documents', :action => 'show', :id => 1},
93 103 :class => 'document')
94 104
95 105 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
96 106 :class => 'version')
97 107
98 108 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
99 109
100 110 source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}
101 111 source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']}
102 112
103 113 to_test = {
104 114 # tickets
105 115 '#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.",
106 116 # changesets
107 117 'r1' => changeset_link,
108 118 # documents
109 119 'document#1' => document_link,
110 120 'document:"Test document"' => document_link,
111 121 # versions
112 122 'version#2' => version_link,
113 123 'version:1.0' => version_link,
114 124 'version:"1.0"' => version_link,
115 125 # source
116 126 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'),
117 127 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".",
118 128 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
119 129 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".",
120 130 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
121 131 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",",
122 132 'source:/some/file@52' => link_to('source:/some/file@52', source_url.merge(:rev => 52), :class => 'source'),
123 133 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'),
124 134 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
125 135 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'),
126 136 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
127 137 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
128 138 # message
129 139 'message#4' => link_to('Post 2', message_url, :class => 'message'),
130 140 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5'), :class => 'message'),
131 141 # escaping
132 142 '!#3.' => '#3.',
133 143 '!r1' => 'r1',
134 144 '!document#1' => 'document#1',
135 145 '!document:"Test document"' => 'document:"Test document"',
136 146 '!version#2' => 'version#2',
137 147 '!version:1.0' => 'version:1.0',
138 148 '!version:"1.0"' => 'version:"1.0"',
139 149 '!source:/some/file' => 'source:/some/file',
140 150 # invalid expressions
141 151 'source:' => 'source:',
142 152 # url hash
143 153 "http://foo.bar/FAQ#3" => '<a class="external" href="http://foo.bar/FAQ#3">http://foo.bar/FAQ#3</a>',
144 154 }
145 155 @project = Project.find(1)
146 156 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
147 157 end
148 158
149 159 def test_wiki_links
150 160 to_test = {
151 161 '[[CookBook documentation]]' => '<a href="/wiki/ecookbook/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
152 162 '[[Another page|Page]]' => '<a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a>',
153 163 # link with anchor
154 164 '[[CookBook documentation#One-section]]' => '<a href="/wiki/ecookbook/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
155 165 '[[Another page#anchor|Page]]' => '<a href="/wiki/ecookbook/Another_page#anchor" class="wiki-page">Page</a>',
156 166 # page that doesn't exist
157 167 '[[Unknown page]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">Unknown page</a>',
158 168 '[[Unknown page|404]]' => '<a href="/wiki/ecookbook/Unknown_page" class="wiki-page new">404</a>',
159 169 # link to another project wiki
160 170 '[[onlinestore:]]' => '<a href="/wiki/onlinestore/" class="wiki-page">onlinestore</a>',
161 171 '[[onlinestore:|Wiki]]' => '<a href="/wiki/onlinestore/" class="wiki-page">Wiki</a>',
162 172 '[[onlinestore:Start page]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Start page</a>',
163 173 '[[onlinestore:Start page|Text]]' => '<a href="/wiki/onlinestore/Start_page" class="wiki-page">Text</a>',
164 174 '[[onlinestore:Unknown page]]' => '<a href="/wiki/onlinestore/Unknown_page" class="wiki-page new">Unknown page</a>',
165 175 # striked through link
166 176 '-[[Another page|Page]]-' => '<del><a href="/wiki/ecookbook/Another_page" class="wiki-page">Page</a></del>',
167 177 # escaping
168 178 '![[Another page|Page]]' => '[[Another page|Page]]',
169 179 }
170 180 @project = Project.find(1)
171 181 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
172 182 end
173 183
174 184 def test_html_tags
175 185 to_test = {
176 186 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
177 187 "<div class=\"bold\">content</div>" => "<p>&lt;div class=\"bold\"&gt;content&lt;/div&gt;</p>",
178 188 "<script>some script;</script>" => "<p>&lt;script&gt;some script;&lt;/script&gt;</p>",
179 189 # do not escape pre/code tags
180 190 "<pre>\nline 1\nline2</pre>" => "<pre>\nline 1\nline2</pre>",
181 191 "<pre><code>\nline 1\nline2</code></pre>" => "<pre><code>\nline 1\nline2</code></pre>",
182 192 "<pre><div>content</div></pre>" => "<pre>&lt;div&gt;content&lt;/div&gt;</pre>",
183 193 "HTML comment: <!-- no comments -->" => "<p>HTML comment: &lt;!-- no comments --&gt;</p>",
184 194 "<!-- opening comment" => "<p>&lt;!-- opening comment</p>",
185 195 # remove attributes
186 196 "<pre class='foo'>some text</pre>" => "<pre>some text</pre>",
187 197 }
188 198 to_test.each { |text, result| assert_equal result, textilizable(text) }
189 199 end
190 200
191 201 def test_allowed_html_tags
192 202 to_test = {
193 203 "<pre>preformatted text</pre>" => "<pre>preformatted text</pre>",
194 204 "<notextile>no *textile* formatting</notextile>" => "no *textile* formatting",
195 205 "<notextile>this is <tag>a tag</tag></notextile>" => "this is &lt;tag&gt;a tag&lt;/tag&gt;"
196 206 }
197 207 to_test.each { |text, result| assert_equal result, textilizable(text) }
198 208 end
199 209
200 210 def test_wiki_links_in_tables
201 211 to_test = {"|[[Page|Link title]]|[[Other Page|Other title]]|\n|Cell 21|[[Last page]]|" =>
202 212 '<tr><td><a href="/wiki/ecookbook/Page" class="wiki-page new">Link title</a></td>' +
203 213 '<td><a href="/wiki/ecookbook/Other_Page" class="wiki-page new">Other title</a></td>' +
204 214 '</tr><tr><td>Cell 21</td><td><a href="/wiki/ecookbook/Last_page" class="wiki-page new">Last page</a></td></tr>'
205 215 }
206 216 @project = Project.find(1)
207 217 to_test.each { |text, result| assert_equal "<table>#{result}</table>", textilizable(text).gsub(/[\t\n]/, '') }
208 218 end
209 219
210 220 def test_text_formatting
211 221 to_test = {'*_+bold, italic and underline+_*' => '<strong><em><ins>bold, italic and underline</ins></em></strong>',
212 222 '(_text within parentheses_)' => '(<em>text within parentheses</em>)'
213 223 }
214 224 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
215 225 end
216 226
217 227 def test_wiki_horizontal_rule
218 228 assert_equal '<hr />', textilizable('---')
219 229 assert_equal '<p>Dashes: ---</p>', textilizable('Dashes: ---')
220 230 end
221 231
222 232 def test_acronym
223 233 assert_equal '<p>This is an acronym: <acronym title="American Civil Liberties Union">ACLU</acronym>.</p>',
224 234 textilizable('This is an acronym: ACLU(American Civil Liberties Union).')
225 235 end
226 236
227 237 def test_footnotes
228 238 raw = <<-RAW
229 239 This is some text[1].
230 240
231 241 fn1. This is the foot note
232 242 RAW
233 243
234 244 expected = <<-EXPECTED
235 245 <p>This is some text<sup><a href=\"#fn1\">1</a></sup>.</p>
236 246 <p id="fn1" class="footnote"><sup>1</sup> This is the foot note</p>
237 247 EXPECTED
238 248
239 249 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
240 250 end
241 251
242 252 def test_table_of_content
243 253 raw = <<-RAW
244 254 {{toc}}
245 255
246 256 h1. Title
247 257
248 258 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
249 259
250 260 h2. Subtitle
251 261
252 262 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
253 263
254 264 h2. Subtitle with %{color:red}red text%
255 265
256 266 h1. Another title
257 267
258 268 RAW
259 269
260 270 expected = '<ul class="toc">' +
261 271 '<li class="heading1"><a href="#Title">Title</a></li>' +
262 272 '<li class="heading2"><a href="#Subtitle">Subtitle</a></li>' +
263 273 '<li class="heading2"><a href="#Subtitle-with-red-text">Subtitle with red text</a></li>' +
264 274 '<li class="heading1"><a href="#Another-title">Another title</a></li>' +
265 275 '</ul>'
266 276
267 277 assert textilizable(raw).gsub("\n", "").include?(expected)
268 278 end
269 279
270 280 def test_blockquote
271 281 # orig raw text
272 282 raw = <<-RAW
273 283 John said:
274 284 > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
275 285 > Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
276 286 > * Donec odio lorem,
277 287 > * sagittis ac,
278 288 > * malesuada in,
279 289 > * adipiscing eu, dolor.
280 290 >
281 291 > >Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.
282 292 > Proin a tellus. Nam vel neque.
283 293
284 294 He's right.
285 295 RAW
286 296
287 297 # expected html
288 298 expected = <<-EXPECTED
289 299 <p>John said:</p>
290 300 <blockquote>
291 301 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
292 302 Nullam commodo metus accumsan nulla. Curabitur lobortis dui id dolor.
293 303 <ul>
294 304 <li>Donec odio lorem,</li>
295 305 <li>sagittis ac,</li>
296 306 <li>malesuada in,</li>
297 307 <li>adipiscing eu, dolor.</li>
298 308 </ul>
299 309 <blockquote>
300 310 <p>Nulla varius pulvinar diam. Proin id arcu id lorem scelerisque condimentum. Proin vehicula turpis vitae lacus.</p>
301 311 </blockquote>
302 312 <p>Proin a tellus. Nam vel neque.</p>
303 313 </blockquote>
304 314 <p>He's right.</p>
305 315 EXPECTED
306 316
307 317 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
308 318 end
309 319
310 320 def test_table
311 321 raw = <<-RAW
312 322 This is a table with empty cells:
313 323
314 324 |cell11|cell12||
315 325 |cell21||cell23|
316 326 |cell31|cell32|cell33|
317 327 RAW
318 328
319 329 expected = <<-EXPECTED
320 330 <p>This is a table with empty cells:</p>
321 331
322 332 <table>
323 333 <tr><td>cell11</td><td>cell12</td><td></td></tr>
324 334 <tr><td>cell21</td><td></td><td>cell23</td></tr>
325 335 <tr><td>cell31</td><td>cell32</td><td>cell33</td></tr>
326 336 </table>
327 337 EXPECTED
328 338
329 339 assert_equal expected.gsub(%r{\s+}, ''), textilizable(raw).gsub(%r{\s+}, '')
330 340 end
331 341
332 342 def test_macro_hello_world
333 343 text = "{{hello_world}}"
334 344 assert textilizable(text).match(/Hello world!/)
335 345 # escaping
336 346 text = "!{{hello_world}}"
337 347 assert_equal '<p>{{hello_world}}</p>', textilizable(text)
338 348 end
339 349
340 350 def test_macro_include
341 351 @project = Project.find(1)
342 352 # include a page of the current project wiki
343 353 text = "{{include(Another page)}}"
344 354 assert textilizable(text).match(/This is a link to a ticket/)
345 355
346 356 @project = nil
347 357 # include a page of a specific project wiki
348 358 text = "{{include(ecookbook:Another page)}}"
349 359 assert textilizable(text).match(/This is a link to a ticket/)
350 360
351 361 text = "{{include(ecookbook:)}}"
352 362 assert textilizable(text).match(/CookBook documentation/)
353 363
354 364 text = "{{include(unknowidentifier:somepage)}}"
355 365 assert textilizable(text).match(/Unknow project/)
356 366 end
357 367
358 368 def test_default_formatter
359 369 Setting.text_formatting = 'unknown'
360 370 text = 'a *link*: http://www.example.net/'
361 371 assert_equal '<p>a *link*: <a href="http://www.example.net/">http://www.example.net/</a></p>', textilizable(text)
362 372 Setting.text_formatting = 'textile'
363 373 end
364 374
365 375 def test_date_format_default
366 376 today = Date.today
367 377 Setting.date_format = ''
368 378 assert_equal l_date(today), format_date(today)
369 379 end
370 380
371 381 def test_date_format
372 382 today = Date.today
373 383 Setting.date_format = '%d %m %Y'
374 384 assert_equal today.strftime('%d %m %Y'), format_date(today)
375 385 end
376 386
377 387 def test_time_format_default
378 388 now = Time.now
379 389 Setting.date_format = ''
380 390 Setting.time_format = ''
381 391 assert_equal l_datetime(now), format_time(now)
382 392 assert_equal l_time(now), format_time(now, false)
383 393 end
384 394
385 395 def test_time_format
386 396 now = Time.now
387 397 Setting.date_format = '%d %m %Y'
388 398 Setting.time_format = '%H %M'
389 399 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now)
390 400 assert_equal now.strftime('%H %M'), format_time(now, false)
391 401 end
392 402
393 403 def test_utc_time_format
394 404 now = Time.now.utc
395 405 Setting.date_format = '%d %m %Y'
396 406 Setting.time_format = '%H %M'
397 407 assert_equal Time.now.strftime('%d %m %Y %H %M'), format_time(now)
398 408 assert_equal Time.now.strftime('%H %M'), format_time(now, false)
399 409 end
400 410
401 411 def test_due_date_distance_in_words
402 412 to_test = { Date.today => 'Due in 0 days',
403 413 Date.today + 1 => 'Due in 1 day',
404 414 Date.today + 100 => 'Due in 100 days',
405 415 Date.today + 20000 => 'Due in 20000 days',
406 416 Date.today - 1 => '1 day late',
407 417 Date.today - 100 => '100 days late',
408 418 Date.today - 20000 => '20000 days late',
409 419 }
410 420 to_test.each do |date, expected|
411 421 assert_equal expected, due_date_distance_in_words(date)
412 422 end
413 423 end
414 424 end
General Comments 0
You need to be logged in to leave comments. Login now