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