##// END OF EJS Templates
Fixed: Textile image with style attribute cause internal server error....
Jean-Philippe Lang -
r1004:5f871e965746
parent child
Show More
@@ -1,407 +1,407
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module ApplicationHelper
18 module ApplicationHelper
19 include Redmine::WikiFormatting::Macros::Definitions
19 include Redmine::WikiFormatting::Macros::Definitions
20
20
21 def current_role
21 def current_role
22 @current_role ||= User.current.role_for_project(@project)
22 @current_role ||= User.current.role_for_project(@project)
23 end
23 end
24
24
25 # Return true if user is authorized for controller/action, otherwise false
25 # Return true if user is authorized for controller/action, otherwise false
26 def authorize_for(controller, action)
26 def authorize_for(controller, action)
27 User.current.allowed_to?({:controller => controller, :action => action}, @project)
27 User.current.allowed_to?({:controller => controller, :action => action}, @project)
28 end
28 end
29
29
30 # Display a link if user is authorized
30 # Display a link if user is authorized
31 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
31 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
32 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
32 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
33 end
33 end
34
34
35 # Display a link to user's account page
35 # Display a link to user's account page
36 def link_to_user(user)
36 def link_to_user(user)
37 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
37 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
38 end
38 end
39
39
40 def link_to_issue(issue)
40 def link_to_issue(issue)
41 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
41 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
42 end
42 end
43
43
44 def toggle_link(name, id, options={})
44 def toggle_link(name, id, options={})
45 onclick = "Element.toggle('#{id}'); "
45 onclick = "Element.toggle('#{id}'); "
46 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
46 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
47 onclick << "return false;"
47 onclick << "return false;"
48 link_to(name, "#", :onclick => onclick)
48 link_to(name, "#", :onclick => onclick)
49 end
49 end
50
50
51 def show_and_goto_link(name, id, options={})
51 def show_and_goto_link(name, id, options={})
52 onclick = "Element.show('#{id}'); "
52 onclick = "Element.show('#{id}'); "
53 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
53 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
54 onclick << "location.href='##{id}-anchor'; "
54 onclick << "location.href='##{id}-anchor'; "
55 onclick << "return false;"
55 onclick << "return false;"
56 link_to(name, "#", options.merge(:onclick => onclick))
56 link_to(name, "#", options.merge(:onclick => onclick))
57 end
57 end
58
58
59 def image_to_function(name, function, html_options = {})
59 def image_to_function(name, function, html_options = {})
60 html_options.symbolize_keys!
60 html_options.symbolize_keys!
61 tag(:input, html_options.merge({
61 tag(:input, html_options.merge({
62 :type => "image", :src => image_path(name),
62 :type => "image", :src => image_path(name),
63 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
63 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
64 }))
64 }))
65 end
65 end
66
66
67 def prompt_to_remote(name, text, param, url, html_options = {})
67 def prompt_to_remote(name, text, param, url, html_options = {})
68 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
68 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
69 link_to name, {}, html_options
69 link_to name, {}, html_options
70 end
70 end
71
71
72 def format_date(date)
72 def format_date(date)
73 return nil unless date
73 return nil unless date
74 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
74 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
75 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
75 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
76 date.strftime(@date_format)
76 date.strftime(@date_format)
77 end
77 end
78
78
79 def format_time(time, include_date = true)
79 def format_time(time, include_date = true)
80 return nil unless time
80 return nil unless time
81 time = time.to_time if time.is_a?(String)
81 time = time.to_time if time.is_a?(String)
82 zone = User.current.time_zone
82 zone = User.current.time_zone
83 if time.utc?
83 if time.utc?
84 local = zone ? zone.adjust(time) : time.getlocal
84 local = zone ? zone.adjust(time) : time.getlocal
85 else
85 else
86 local = zone ? zone.adjust(time.getutc) : time
86 local = zone ? zone.adjust(time.getutc) : time
87 end
87 end
88 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
88 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
89 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
89 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
90 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
90 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
91 end
91 end
92
92
93 def authoring(created, author)
93 def authoring(created, author)
94 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
94 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
95 l(:label_added_time_by, author || 'Anonymous', time_tag)
95 l(:label_added_time_by, author || 'Anonymous', time_tag)
96 end
96 end
97
97
98 def day_name(day)
98 def day_name(day)
99 l(:general_day_names).split(',')[day-1]
99 l(:general_day_names).split(',')[day-1]
100 end
100 end
101
101
102 def month_name(month)
102 def month_name(month)
103 l(:actionview_datehelper_select_month_names).split(',')[month-1]
103 l(:actionview_datehelper_select_month_names).split(',')[month-1]
104 end
104 end
105
105
106 def pagination_links_full(paginator, options={}, html_options={})
106 def pagination_links_full(paginator, options={}, html_options={})
107 page_param = options.delete(:page_param) || :page
107 page_param = options.delete(:page_param) || :page
108
108
109 html = ''
109 html = ''
110 html << link_to_remote(('&#171; ' + l(:label_previous)),
110 html << link_to_remote(('&#171; ' + l(:label_previous)),
111 {:update => "content", :url => options.merge(page_param => paginator.current.previous)},
111 {:update => "content", :url => options.merge(page_param => paginator.current.previous)},
112 {:href => url_for(:params => options.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
112 {:href => url_for(:params => options.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
113
113
114 html << (pagination_links_each(paginator, options) do |n|
114 html << (pagination_links_each(paginator, options) do |n|
115 link_to_remote(n.to_s,
115 link_to_remote(n.to_s,
116 {:url => {:params => options.merge(page_param => n)}, :update => 'content'},
116 {:url => {:params => options.merge(page_param => n)}, :update => 'content'},
117 {:href => url_for(:params => options.merge(page_param => n))})
117 {:href => url_for(:params => options.merge(page_param => n))})
118 end || '')
118 end || '')
119
119
120 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
120 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
121 {:update => "content", :url => options.merge(page_param => paginator.current.next)},
121 {:update => "content", :url => options.merge(page_param => paginator.current.next)},
122 {:href => url_for(:params => options.merge(page_param => paginator.current.next))}) if paginator.current.next
122 {:href => url_for(:params => options.merge(page_param => paginator.current.next))}) if paginator.current.next
123 html
123 html
124 end
124 end
125
125
126 def set_html_title(text)
126 def set_html_title(text)
127 @html_header_title = text
127 @html_header_title = text
128 end
128 end
129
129
130 def html_title
130 def html_title
131 title = []
131 title = []
132 title << @project.name if @project
132 title << @project.name if @project
133 title << @html_header_title
133 title << @html_header_title
134 title << Setting.app_title
134 title << Setting.app_title
135 title.compact.join(' - ')
135 title.compact.join(' - ')
136 end
136 end
137
137
138 ACCESSKEYS = {:edit => 'e',
138 ACCESSKEYS = {:edit => 'e',
139 :preview => 'r',
139 :preview => 'r',
140 :quick_search => 'f',
140 :quick_search => 'f',
141 :search => '4',
141 :search => '4',
142 }.freeze unless const_defined?(:ACCESSKEYS)
142 }.freeze unless const_defined?(:ACCESSKEYS)
143
143
144 def accesskey(s)
144 def accesskey(s)
145 ACCESSKEYS[s]
145 ACCESSKEYS[s]
146 end
146 end
147
147
148 # Formats text according to system settings.
148 # Formats text according to system settings.
149 # 2 ways to call this method:
149 # 2 ways to call this method:
150 # * with a String: textilizable(text, options)
150 # * with a String: textilizable(text, options)
151 # * with an object and one of its attribute: textilizable(issue, :description, options)
151 # * with an object and one of its attribute: textilizable(issue, :description, options)
152 def textilizable(*args)
152 def textilizable(*args)
153 options = args.last.is_a?(Hash) ? args.pop : {}
153 options = args.last.is_a?(Hash) ? args.pop : {}
154 case args.size
154 case args.size
155 when 1
155 when 1
156 obj = nil
156 obj = nil
157 text = args.shift || ''
157 text = args.shift || ''
158 when 2
158 when 2
159 obj = args.shift
159 obj = args.shift
160 text = obj.send(args.shift)
160 text = obj.send(args.shift)
161 else
161 else
162 raise ArgumentError, 'invalid arguments to textilizable'
162 raise ArgumentError, 'invalid arguments to textilizable'
163 end
163 end
164
164
165 # when using an image link, try to use an attachment, if possible
165 # when using an image link, try to use an attachment, if possible
166 attachments = options[:attachments]
166 attachments = options[:attachments]
167 if attachments
167 if attachments
168 text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
168 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
169 align = $1
169 style = $1
170 filename = $2
170 filename = $6
171 rf = Regexp.new(filename, Regexp::IGNORECASE)
171 rf = Regexp.new(filename, Regexp::IGNORECASE)
172 # search for the picture in attachments
172 # search for the picture in attachments
173 if found = attachments.detect { |att| att.filename =~ rf }
173 if found = attachments.detect { |att| att.filename =~ rf }
174 image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id
174 image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id
175 "!#{align}#{image_url}!"
175 "!#{style}#{image_url}!"
176 else
176 else
177 "!#{align}#{filename}!"
177 "!#{style}#{filename}!"
178 end
178 end
179 end
179 end
180 end
180 end
181
181
182 text = (Setting.text_formatting == 'textile') ?
182 text = (Setting.text_formatting == 'textile') ?
183 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
183 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
184 simple_format(auto_link(h(text)))
184 simple_format(auto_link(h(text)))
185
185
186 # different methods for formatting wiki links
186 # different methods for formatting wiki links
187 case options[:wiki_links]
187 case options[:wiki_links]
188 when :local
188 when :local
189 # used for local links to html files
189 # used for local links to html files
190 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
190 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
191 when :anchor
191 when :anchor
192 # used for single-file wiki export
192 # used for single-file wiki export
193 format_wiki_link = Proc.new {|project, title| "##{title}" }
193 format_wiki_link = Proc.new {|project, title| "##{title}" }
194 else
194 else
195 format_wiki_link = Proc.new {|project, title| url_for :controller => 'wiki', :action => 'index', :id => project, :page => title }
195 format_wiki_link = Proc.new {|project, title| url_for :controller => 'wiki', :action => 'index', :id => project, :page => title }
196 end
196 end
197
197
198 project = options[:project] || @project
198 project = options[:project] || @project
199
199
200 # turn wiki links into html links
200 # turn wiki links into html links
201 # example:
201 # example:
202 # [[mypage]]
202 # [[mypage]]
203 # [[mypage|mytext]]
203 # [[mypage|mytext]]
204 # wiki links can refer other project wikis, using project name or identifier:
204 # wiki links can refer other project wikis, using project name or identifier:
205 # [[project:]] -> wiki starting page
205 # [[project:]] -> wiki starting page
206 # [[project:|mytext]]
206 # [[project:|mytext]]
207 # [[project:mypage]]
207 # [[project:mypage]]
208 # [[project:mypage|mytext]]
208 # [[project:mypage|mytext]]
209 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) do |m|
209 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) do |m|
210 link_project = project
210 link_project = project
211 page = $1
211 page = $1
212 title = $3
212 title = $3
213 if page =~ /^([^\:]+)\:(.*)$/
213 if page =~ /^([^\:]+)\:(.*)$/
214 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
214 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
215 page = title || $2
215 page = title || $2
216 title = $1 if page.blank?
216 title = $1 if page.blank?
217 end
217 end
218
218
219 if link_project && link_project.wiki
219 if link_project && link_project.wiki
220 # check if page exists
220 # check if page exists
221 wiki_page = link_project.wiki.find_page(page)
221 wiki_page = link_project.wiki.find_page(page)
222 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
222 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
223 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
223 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
224 else
224 else
225 # project or wiki doesn't exist
225 # project or wiki doesn't exist
226 title || page
226 title || page
227 end
227 end
228 end
228 end
229
229
230 # turn issue and revision ids into links
230 # turn issue and revision ids into links
231 # example:
231 # example:
232 # #52 -> <a href="/issues/show/52">#52</a>
232 # #52 -> <a href="/issues/show/52">#52</a>
233 # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (project.id is 6)
233 # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (project.id is 6)
234 text = text.gsub(%r{([\s\(,-^])(#|r)(\d+)(?=[[:punct:]]|\s|<|$)}) do |m|
234 text = text.gsub(%r{([\s\(,-^])(#|r)(\d+)(?=[[:punct:]]|\s|<|$)}) do |m|
235 leading, otype, oid = $1, $2, $3
235 leading, otype, oid = $1, $2, $3
236 link = nil
236 link = nil
237 if otype == 'r'
237 if otype == 'r'
238 if project && (changeset = project.changesets.find_by_revision(oid))
238 if project && (changeset = project.changesets.find_by_revision(oid))
239 link = link_to("r#{oid}", {:controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid}, :class => 'changeset',
239 link = link_to("r#{oid}", {:controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid}, :class => 'changeset',
240 :title => truncate(changeset.comments, 100))
240 :title => truncate(changeset.comments, 100))
241 end
241 end
242 else
242 else
243 if issue = Issue.find_by_id(oid.to_i, :include => [:project, :status], :conditions => Project.visible_by(User.current))
243 if issue = Issue.find_by_id(oid.to_i, :include => [:project, :status], :conditions => Project.visible_by(User.current))
244 link = link_to("##{oid}", {:controller => 'issues', :action => 'show', :id => oid}, :class => 'issue',
244 link = link_to("##{oid}", {:controller => 'issues', :action => 'show', :id => oid}, :class => 'issue',
245 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
245 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
246 link = content_tag('del', link) if issue.closed?
246 link = content_tag('del', link) if issue.closed?
247 end
247 end
248 end
248 end
249 leading + (link || "#{otype}#{oid}")
249 leading + (link || "#{otype}#{oid}")
250 end
250 end
251
251
252 text
252 text
253 end
253 end
254
254
255 # Same as Rails' simple_format helper without using paragraphs
255 # Same as Rails' simple_format helper without using paragraphs
256 def simple_format_without_paragraph(text)
256 def simple_format_without_paragraph(text)
257 text.to_s.
257 text.to_s.
258 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
258 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
259 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
259 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
260 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
260 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
261 end
261 end
262
262
263 def error_messages_for(object_name, options = {})
263 def error_messages_for(object_name, options = {})
264 options = options.symbolize_keys
264 options = options.symbolize_keys
265 object = instance_variable_get("@#{object_name}")
265 object = instance_variable_get("@#{object_name}")
266 if object && !object.errors.empty?
266 if object && !object.errors.empty?
267 # build full_messages here with controller current language
267 # build full_messages here with controller current language
268 full_messages = []
268 full_messages = []
269 object.errors.each do |attr, msg|
269 object.errors.each do |attr, msg|
270 next if msg.nil?
270 next if msg.nil?
271 msg = msg.first if msg.is_a? Array
271 msg = msg.first if msg.is_a? Array
272 if attr == "base"
272 if attr == "base"
273 full_messages << l(msg)
273 full_messages << l(msg)
274 else
274 else
275 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
275 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
276 end
276 end
277 end
277 end
278 # retrieve custom values error messages
278 # retrieve custom values error messages
279 if object.errors[:custom_values]
279 if object.errors[:custom_values]
280 object.custom_values.each do |v|
280 object.custom_values.each do |v|
281 v.errors.each do |attr, msg|
281 v.errors.each do |attr, msg|
282 next if msg.nil?
282 next if msg.nil?
283 msg = msg.first if msg.is_a? Array
283 msg = msg.first if msg.is_a? Array
284 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
284 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
285 end
285 end
286 end
286 end
287 end
287 end
288 content_tag("div",
288 content_tag("div",
289 content_tag(
289 content_tag(
290 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
290 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
291 ) +
291 ) +
292 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
292 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
293 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
293 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
294 )
294 )
295 else
295 else
296 ""
296 ""
297 end
297 end
298 end
298 end
299
299
300 def lang_options_for_select(blank=true)
300 def lang_options_for_select(blank=true)
301 (blank ? [["(auto)", ""]] : []) +
301 (blank ? [["(auto)", ""]] : []) +
302 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
302 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
303 end
303 end
304
304
305 def label_tag_for(name, option_tags = nil, options = {})
305 def label_tag_for(name, option_tags = nil, options = {})
306 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
306 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
307 content_tag("label", label_text)
307 content_tag("label", label_text)
308 end
308 end
309
309
310 def labelled_tabular_form_for(name, object, options, &proc)
310 def labelled_tabular_form_for(name, object, options, &proc)
311 options[:html] ||= {}
311 options[:html] ||= {}
312 options[:html].store :class, "tabular"
312 options[:html].store :class, "tabular"
313 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
313 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
314 end
314 end
315
315
316 def check_all_links(form_name)
316 def check_all_links(form_name)
317 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
317 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
318 " | " +
318 " | " +
319 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
319 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
320 end
320 end
321
321
322 def progress_bar(pcts, options={})
322 def progress_bar(pcts, options={})
323 pcts = [pcts, pcts] unless pcts.is_a?(Array)
323 pcts = [pcts, pcts] unless pcts.is_a?(Array)
324 pcts[1] = pcts[1] - pcts[0]
324 pcts[1] = pcts[1] - pcts[0]
325 pcts << (100 - pcts[1] - pcts[0])
325 pcts << (100 - pcts[1] - pcts[0])
326 width = options[:width] || '100px;'
326 width = options[:width] || '100px;'
327 legend = options[:legend] || ''
327 legend = options[:legend] || ''
328 content_tag('table',
328 content_tag('table',
329 content_tag('tr',
329 content_tag('tr',
330 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
330 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
331 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
331 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
332 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
332 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
333 ), :class => 'progress', :style => "width: #{width};") +
333 ), :class => 'progress', :style => "width: #{width};") +
334 content_tag('p', legend, :class => 'pourcent')
334 content_tag('p', legend, :class => 'pourcent')
335 end
335 end
336
336
337 def context_menu_link(name, url, options={})
337 def context_menu_link(name, url, options={})
338 options[:class] ||= ''
338 options[:class] ||= ''
339 if options.delete(:selected)
339 if options.delete(:selected)
340 options[:class] << ' icon-checked disabled'
340 options[:class] << ' icon-checked disabled'
341 options[:disabled] = true
341 options[:disabled] = true
342 end
342 end
343 if options.delete(:disabled)
343 if options.delete(:disabled)
344 options.delete(:method)
344 options.delete(:method)
345 options.delete(:confirm)
345 options.delete(:confirm)
346 options.delete(:onclick)
346 options.delete(:onclick)
347 options[:class] << ' disabled'
347 options[:class] << ' disabled'
348 url = '#'
348 url = '#'
349 end
349 end
350 link_to name, url, options
350 link_to name, url, options
351 end
351 end
352
352
353 def calendar_for(field_id)
353 def calendar_for(field_id)
354 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
354 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
355 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
355 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
356 end
356 end
357
357
358 def wikitoolbar_for(field_id)
358 def wikitoolbar_for(field_id)
359 return '' unless Setting.text_formatting == 'textile'
359 return '' unless Setting.text_formatting == 'textile'
360 javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
360 javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
361 end
361 end
362
362
363 def content_for(name, content = nil, &block)
363 def content_for(name, content = nil, &block)
364 @has_content ||= {}
364 @has_content ||= {}
365 @has_content[name] = true
365 @has_content[name] = true
366 super(name, content, &block)
366 super(name, content, &block)
367 end
367 end
368
368
369 def has_content?(name)
369 def has_content?(name)
370 (@has_content && @has_content[name]) || false
370 (@has_content && @has_content[name]) || false
371 end
371 end
372 end
372 end
373
373
374 class TabularFormBuilder < ActionView::Helpers::FormBuilder
374 class TabularFormBuilder < ActionView::Helpers::FormBuilder
375 include GLoc
375 include GLoc
376
376
377 def initialize(object_name, object, template, options, proc)
377 def initialize(object_name, object, template, options, proc)
378 set_language_if_valid options.delete(:lang)
378 set_language_if_valid options.delete(:lang)
379 @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
379 @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
380 end
380 end
381
381
382 (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
382 (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
383 src = <<-END_SRC
383 src = <<-END_SRC
384 def #{selector}(field, options = {})
384 def #{selector}(field, options = {})
385 return super if options.delete :no_label
385 return super if options.delete :no_label
386 label_text = l(options[:label]) if options[:label]
386 label_text = l(options[:label]) if options[:label]
387 label_text ||= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym)
387 label_text ||= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym)
388 label_text << @template.content_tag("span", " *", :class => "required") if options.delete(:required)
388 label_text << @template.content_tag("span", " *", :class => "required") if options.delete(:required)
389 label = @template.content_tag("label", label_text,
389 label = @template.content_tag("label", label_text,
390 :class => (@object && @object.errors[field] ? "error" : nil),
390 :class => (@object && @object.errors[field] ? "error" : nil),
391 :for => (@object_name.to_s + "_" + field.to_s))
391 :for => (@object_name.to_s + "_" + field.to_s))
392 label + super
392 label + super
393 end
393 end
394 END_SRC
394 END_SRC
395 class_eval src, __FILE__, __LINE__
395 class_eval src, __FILE__, __LINE__
396 end
396 end
397
397
398 def select(field, choices, options = {}, html_options = {})
398 def select(field, choices, options = {}, html_options = {})
399 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
399 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
400 label = @template.content_tag("label", label_text,
400 label = @template.content_tag("label", label_text,
401 :class => (@object && @object.errors[field] ? "error" : nil),
401 :class => (@object && @object.errors[field] ? "error" : nil),
402 :for => (@object_name.to_s + "_" + field.to_s))
402 :for => (@object_name.to_s + "_" + field.to_s))
403 label + super
403 label + super
404 end
404 end
405
405
406 end
406 end
407
407
@@ -1,160 +1,161
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'redcloth'
18 require 'redcloth'
19 require 'coderay'
19 require 'coderay'
20
20
21 module Redmine
21 module Redmine
22 module WikiFormatting
22 module WikiFormatting
23
23
24 private
24 private
25
25
26 class TextileFormatter < RedCloth
26 class TextileFormatter < RedCloth
27
27
28 RULES = [:inline_auto_link, :inline_auto_mailto, :textile, :inline_toc, :inline_macros]
28 # auto_link rule after textile rules so that it doesn't break !image_url! tags
29 RULES = [:textile, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
29
30
30 def initialize(*args)
31 def initialize(*args)
31 super
32 super
32 self.hard_breaks=true
33 self.hard_breaks=true
33 self.no_span_caps=true
34 self.no_span_caps=true
34 end
35 end
35
36
36 def to_html(*rules, &block)
37 def to_html(*rules, &block)
37 @toc = []
38 @toc = []
38 @macros_runner = block
39 @macros_runner = block
39 super(*RULES).to_s
40 super(*RULES).to_s
40 end
41 end
41
42
42 private
43 private
43
44
44 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
45 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
45 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
46 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
46 def hard_break( text )
47 def hard_break( text )
47 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
48 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
48 end
49 end
49
50
50 # Patch to add code highlighting support to RedCloth
51 # Patch to add code highlighting support to RedCloth
51 def smooth_offtags( text )
52 def smooth_offtags( text )
52 unless @pre_list.empty?
53 unless @pre_list.empty?
53 ## replace <pre> content
54 ## replace <pre> content
54 text.gsub!(/<redpre#(\d+)>/) do
55 text.gsub!(/<redpre#(\d+)>/) do
55 content = @pre_list[$1.to_i]
56 content = @pre_list[$1.to_i]
56 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
57 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
57 content = "<code class=\"#{$1} CodeRay\">" +
58 content = "<code class=\"#{$1} CodeRay\">" +
58 CodeRay.scan($2, $1).html(:escape => false, :line_numbers => :inline)
59 CodeRay.scan($2, $1).html(:escape => false, :line_numbers => :inline)
59 end
60 end
60 content
61 content
61 end
62 end
62 end
63 end
63 end
64 end
64
65
65 # Patch to add 'table of content' support to RedCloth
66 # Patch to add 'table of content' support to RedCloth
66 def textile_p_withtoc(tag, atts, cite, content)
67 def textile_p_withtoc(tag, atts, cite, content)
67 if tag =~ /^h(\d)$/
68 if tag =~ /^h(\d)$/
68 @toc << [$1.to_i, content]
69 @toc << [$1.to_i, content]
69 end
70 end
70 content = "<a name=\"#{@toc.length}\" class=\"wiki-page\"></a>" + content
71 content = "<a name=\"#{@toc.length}\" class=\"wiki-page\"></a>" + content
71 textile_p(tag, atts, cite, content)
72 textile_p(tag, atts, cite, content)
72 end
73 end
73
74
74 alias :textile_h1 :textile_p_withtoc
75 alias :textile_h1 :textile_p_withtoc
75 alias :textile_h2 :textile_p_withtoc
76 alias :textile_h2 :textile_p_withtoc
76 alias :textile_h3 :textile_p_withtoc
77 alias :textile_h3 :textile_p_withtoc
77
78
78 def inline_toc(text)
79 def inline_toc(text)
79 text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
80 text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
80 div_class = 'toc'
81 div_class = 'toc'
81 div_class << ' right' if $1 == '>'
82 div_class << ' right' if $1 == '>'
82 div_class << ' left' if $1 == '<'
83 div_class << ' left' if $1 == '<'
83 out = "<div class=\"#{div_class}\">"
84 out = "<div class=\"#{div_class}\">"
84 @toc.each_with_index do |heading, index|
85 @toc.each_with_index do |heading, index|
85 # remove wiki links from the item
86 # remove wiki links from the item
86 toc_item = heading.last.gsub(/(\[\[|\]\])/, '')
87 toc_item = heading.last.gsub(/(\[\[|\]\])/, '')
87 out << "<a href=\"##{index+1}\" class=\"heading#{heading.first}\">#{toc_item}</a>"
88 out << "<a href=\"##{index+1}\" class=\"heading#{heading.first}\">#{toc_item}</a>"
88 end
89 end
89 out << '</div>'
90 out << '</div>'
90 out
91 out
91 end
92 end
92 end
93 end
93
94
94 MACROS_RE = /
95 MACROS_RE = /
95 \{\{ # opening tag
96 \{\{ # opening tag
96 ([\w]+) # macro name
97 ([\w]+) # macro name
97 (\(([^\}]*)\))? # optional arguments
98 (\(([^\}]*)\))? # optional arguments
98 \}\} # closing tag
99 \}\} # closing tag
99 /x unless const_defined?(:MACROS_RE)
100 /x unless const_defined?(:MACROS_RE)
100
101
101 def inline_macros(text)
102 def inline_macros(text)
102 text.gsub!(MACROS_RE) do
103 text.gsub!(MACROS_RE) do
103 all, macro = $&, $1.downcase
104 all, macro = $&, $1.downcase
104 args = ($3 || '').split(',').each(&:strip)
105 args = ($3 || '').split(',').each(&:strip)
105 begin
106 begin
106 @macros_runner.call(macro, args)
107 @macros_runner.call(macro, args)
107 rescue => e
108 rescue => e
108 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
109 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
109 end || all
110 end || all
110 end
111 end
111 end
112 end
112
113
113 AUTO_LINK_RE = %r{
114 AUTO_LINK_RE = %r{
114 ( # leading text
115 ( # leading text
115 <\w+.*?>| # leading HTML tag, or
116 <\w+.*?>| # leading HTML tag, or
116 [^=<>!:'"/]| # leading punctuation, or
117 [^=<>!:'"/]| # leading punctuation, or
117 ^ # beginning of line
118 ^ # beginning of line
118 )
119 )
119 (
120 (
120 (?:https?://)| # protocol spec, or
121 (?:https?://)| # protocol spec, or
121 (?:www\.) # www.*
122 (?:www\.) # www.*
122 )
123 )
123 (
124 (
124 (\S+?) # url
125 (\S+?) # url
125 (\/)? # slash
126 (\/)? # slash
126 )
127 )
127 ([^\w\=\/;]*?) # post
128 ([^\w\=\/;]*?) # post
128 (?=<|\s|$)
129 (?=<|\s|$)
129 }x unless const_defined?(:AUTO_LINK_RE)
130 }x unless const_defined?(:AUTO_LINK_RE)
130
131
131 # Turns all urls into clickable links (code from Rails).
132 # Turns all urls into clickable links (code from Rails).
132 def inline_auto_link(text)
133 def inline_auto_link(text)
133 text.gsub!(AUTO_LINK_RE) do
134 text.gsub!(AUTO_LINK_RE) do
134 all, leading, proto, url, post = $&, $1, $2, $3, $6
135 all, leading, proto, url, post = $&, $1, $2, $3, $6
135 if leading =~ /<a\s/i || leading =~ /![<>=]?/
136 if leading =~ /<a\s/i || leading =~ /![<>=]?/
136 # don't replace URL's that are already linked
137 # don't replace URL's that are already linked
137 # and URL's prefixed with ! !> !< != (textile images)
138 # and URL's prefixed with ! !> !< != (textile images)
138 all
139 all
139 else
140 else
140 %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
141 %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
141 end
142 end
142 end
143 end
143 end
144 end
144
145
145 # Turns all email addresses into clickable links (code from Rails).
146 # Turns all email addresses into clickable links (code from Rails).
146 def inline_auto_mailto(text)
147 def inline_auto_mailto(text)
147 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
148 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
148 text = $1
149 text = $1
149 %{<a href="mailto:#{$1}" class="email">#{text}</a>}
150 %{<a href="mailto:#{$1}" class="email">#{text}</a>}
150 end
151 end
151 end
152 end
152 end
153 end
153
154
154 public
155 public
155
156
156 def self.to_html(text, options = {}, &block)
157 def self.to_html(text, options = {}, &block)
157 TextileFormatter.new(text).to_html(&block)
158 TextileFormatter.new(text).to_html(&block)
158 end
159 end
159 end
160 end
160 end
161 end
@@ -1,106 +1,112
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
23 fixtures :projects, :repositories, :changesets, :trackers, :issue_statuses, :issues
24
24
25 def setup
25 def setup
26 super
26 super
27 end
27 end
28
28
29 def test_auto_links
29 def test_auto_links
30 to_test = {
30 to_test = {
31 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
31 'http://foo.bar' => '<a class="external" href="http://foo.bar">http://foo.bar</a>',
32 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
32 'http://foo.bar/~user' => '<a class="external" href="http://foo.bar/~user">http://foo.bar/~user</a>',
33 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
33 'http://foo.bar.' => '<a class="external" href="http://foo.bar">http://foo.bar</a>.',
34 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
34 'http://foo.bar/foo.bar#foo.bar.' => '<a class="external" href="http://foo.bar/foo.bar#foo.bar">http://foo.bar/foo.bar#foo.bar</a>.',
35 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
35 'www.foo.bar' => '<a class="external" href="http://www.foo.bar">www.foo.bar</a>',
36 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
36 'http://foo.bar/page?p=1&t=z&s=' => '<a class="external" href="http://foo.bar/page?p=1&#38;t=z&#38;s=">http://foo.bar/page?p=1&#38;t=z&#38;s=</a>',
37 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>'
37 'http://foo.bar/page#125' => '<a class="external" href="http://foo.bar/page#125">http://foo.bar/page#125</a>'
38 }
38 }
39 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
39 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
40 end
40 end
41
41
42 def test_auto_mailto
42 def test_auto_mailto
43 assert_equal '<p><a href="mailto:test@foo.bar" class="email">test@foo.bar</a></p>',
43 assert_equal '<p><a href="mailto:test@foo.bar" class="email">test@foo.bar</a></p>',
44 textilizable('test@foo.bar')
44 textilizable('test@foo.bar')
45 end
45 end
46
46
47 def test_textile_tags
47 def test_inline_images
48 to_test = {
48 to_test = {
49 # inline images
50 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
49 '!http://foo.bar/image.jpg!' => '<img src="http://foo.bar/image.jpg" alt="" />',
51 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
50 'floating !>http://foo.bar/image.jpg!' => 'floating <div style="float:right"><img src="http://foo.bar/image.jpg" alt="" /></div>',
52 # textile links
51 'with class !(some-class)http://foo.bar/image.jpg!' => 'with class <img src="http://foo.bar/image.jpg" class="some-class" alt="" />',
52 'with style !{width:100px;height100px}http://foo.bar/image.jpg!' => 'with style <img src="http://foo.bar/image.jpg" style="width:100px;height100px;" alt="" />',
53 }
54 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
55 end
56
57 def test_textile_external_links
58 to_test = {
53 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
59 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
54 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
60 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
55 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>'
61 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>'
56 }
62 }
57 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
63 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
58 end
64 end
59
65
60 def test_redmine_links
66 def test_redmine_links
61 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
67 issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3},
62 :class => 'issue', :title => 'Error 281 when updating a recipe (New)')
68 :class => 'issue', :title => 'Error 281 when updating a recipe (New)')
63 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 1, :rev => 1},
69 changeset_link = link_to('r1', {:controller => 'repositories', :action => 'revision', :id => 1, :rev => 1},
64 :class => 'changeset', :title => 'My very first commit')
70 :class => 'changeset', :title => 'My very first commit')
65
71
66 to_test = {
72 to_test = {
67 '#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.",
73 '#3, #3 and #3.' => "#{issue_link}, #{issue_link} and #{issue_link}.",
68 'r1' => changeset_link
74 'r1' => changeset_link
69 }
75 }
70 @project = Project.find(1)
76 @project = Project.find(1)
71 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
77 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
72 end
78 end
73
79
74 def test_macro_hello_world
80 def test_macro_hello_world
75 text = "{{hello_world}}"
81 text = "{{hello_world}}"
76 assert textilizable(text).match(/Hello world!/)
82 assert textilizable(text).match(/Hello world!/)
77 end
83 end
78
84
79 def test_date_format_default
85 def test_date_format_default
80 today = Date.today
86 today = Date.today
81 Setting.date_format = ''
87 Setting.date_format = ''
82 assert_equal l_date(today), format_date(today)
88 assert_equal l_date(today), format_date(today)
83 end
89 end
84
90
85 def test_date_format
91 def test_date_format
86 today = Date.today
92 today = Date.today
87 Setting.date_format = '%d %m %Y'
93 Setting.date_format = '%d %m %Y'
88 assert_equal today.strftime('%d %m %Y'), format_date(today)
94 assert_equal today.strftime('%d %m %Y'), format_date(today)
89 end
95 end
90
96
91 def test_time_format_default
97 def test_time_format_default
92 now = Time.now
98 now = Time.now
93 Setting.date_format = ''
99 Setting.date_format = ''
94 Setting.time_format = ''
100 Setting.time_format = ''
95 assert_equal l_datetime(now), format_time(now)
101 assert_equal l_datetime(now), format_time(now)
96 assert_equal l_time(now), format_time(now, false)
102 assert_equal l_time(now), format_time(now, false)
97 end
103 end
98
104
99 def test_time_format
105 def test_time_format
100 now = Time.now
106 now = Time.now
101 Setting.date_format = '%d %m %Y'
107 Setting.date_format = '%d %m %Y'
102 Setting.time_format = '%H %M'
108 Setting.time_format = '%H %M'
103 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now)
109 assert_equal now.strftime('%d %m %Y %H %M'), format_time(now)
104 assert_equal now.strftime('%H %M'), format_time(now, false)
110 assert_equal now.strftime('%H %M'), format_time(now, false)
105 end
111 end
106 end
112 end
General Comments 0
You need to be logged in to leave comments. Login now