##// END OF EJS Templates
Escape image filename regexp (#1971)....
Jean-Philippe Lang -
r1922:c4419e268f01
parent child
Show More
@@ -1,571 +1,571
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 remote if user is authorized
38 # Display a link to remote if user is authorized
39 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
39 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
40 url = options[:url] || {}
40 url = options[:url] || {}
41 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
41 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
42 end
42 end
43
43
44 # Display a link to user's account page
44 # Display a link to user's account page
45 def link_to_user(user)
45 def link_to_user(user)
46 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
46 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
47 end
47 end
48
48
49 def link_to_issue(issue, options={})
49 def link_to_issue(issue, options={})
50 options[:class] ||= ''
50 options[:class] ||= ''
51 options[:class] << ' issue'
51 options[:class] << ' issue'
52 options[:class] << ' closed' if issue.closed?
52 options[:class] << ' closed' if issue.closed?
53 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
53 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
54 end
54 end
55
55
56 # Generates a link to an attachment.
56 # Generates a link to an attachment.
57 # Options:
57 # Options:
58 # * :text - Link text (default to attachment filename)
58 # * :text - Link text (default to attachment filename)
59 # * :download - Force download (default: false)
59 # * :download - Force download (default: false)
60 def link_to_attachment(attachment, options={})
60 def link_to_attachment(attachment, options={})
61 text = options.delete(:text) || attachment.filename
61 text = options.delete(:text) || attachment.filename
62 action = options.delete(:download) ? 'download' : 'show'
62 action = options.delete(:download) ? 'download' : 'show'
63
63
64 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
64 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
65 end
65 end
66
66
67 def toggle_link(name, id, options={})
67 def toggle_link(name, id, options={})
68 onclick = "Element.toggle('#{id}'); "
68 onclick = "Element.toggle('#{id}'); "
69 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
69 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
70 onclick << "return false;"
70 onclick << "return false;"
71 link_to(name, "#", :onclick => onclick)
71 link_to(name, "#", :onclick => onclick)
72 end
72 end
73
73
74 def image_to_function(name, function, html_options = {})
74 def image_to_function(name, function, html_options = {})
75 html_options.symbolize_keys!
75 html_options.symbolize_keys!
76 tag(:input, html_options.merge({
76 tag(:input, html_options.merge({
77 :type => "image", :src => image_path(name),
77 :type => "image", :src => image_path(name),
78 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
78 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
79 }))
79 }))
80 end
80 end
81
81
82 def prompt_to_remote(name, text, param, url, html_options = {})
82 def prompt_to_remote(name, text, param, url, html_options = {})
83 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
83 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
84 link_to name, {}, html_options
84 link_to name, {}, html_options
85 end
85 end
86
86
87 def format_date(date)
87 def format_date(date)
88 return nil unless date
88 return nil unless date
89 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
89 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
90 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
90 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
91 date.strftime(@date_format)
91 date.strftime(@date_format)
92 end
92 end
93
93
94 def format_time(time, include_date = true)
94 def format_time(time, include_date = true)
95 return nil unless time
95 return nil unless time
96 time = time.to_time if time.is_a?(String)
96 time = time.to_time if time.is_a?(String)
97 zone = User.current.time_zone
97 zone = User.current.time_zone
98 local = zone ? time.in_time_zone(zone) : (time.utc? ? time.localtime : time)
98 local = zone ? time.in_time_zone(zone) : (time.utc? ? time.localtime : time)
99 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
99 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
100 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
100 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
101 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
101 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
102 end
102 end
103
103
104 def distance_of_date_in_words(from_date, to_date = 0)
104 def distance_of_date_in_words(from_date, to_date = 0)
105 from_date = from_date.to_date if from_date.respond_to?(:to_date)
105 from_date = from_date.to_date if from_date.respond_to?(:to_date)
106 to_date = to_date.to_date if to_date.respond_to?(:to_date)
106 to_date = to_date.to_date if to_date.respond_to?(:to_date)
107 distance_in_days = (to_date - from_date).abs
107 distance_in_days = (to_date - from_date).abs
108 lwr(:actionview_datehelper_time_in_words_day, distance_in_days)
108 lwr(:actionview_datehelper_time_in_words_day, distance_in_days)
109 end
109 end
110
110
111 def due_date_distance_in_words(date)
111 def due_date_distance_in_words(date)
112 if date
112 if date
113 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
113 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
114 end
114 end
115 end
115 end
116
116
117 # Truncates and returns the string as a single line
117 # Truncates and returns the string as a single line
118 def truncate_single_line(string, *args)
118 def truncate_single_line(string, *args)
119 truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
119 truncate(string, *args).gsub(%r{[\r\n]+}m, ' ')
120 end
120 end
121
121
122 def html_hours(text)
122 def html_hours(text)
123 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
123 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
124 end
124 end
125
125
126 def authoring(created, author)
126 def authoring(created, author)
127 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
127 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
128 author_tag = (author.is_a?(User) && !author.anonymous?) ? link_to(h(author), :controller => 'account', :action => 'show', :id => author) : h(author || 'Anonymous')
128 author_tag = (author.is_a?(User) && !author.anonymous?) ? link_to(h(author), :controller => 'account', :action => 'show', :id => author) : h(author || 'Anonymous')
129 l(:label_added_time_by, author_tag, time_tag)
129 l(:label_added_time_by, author_tag, time_tag)
130 end
130 end
131
131
132 def l_or_humanize(s)
132 def l_or_humanize(s)
133 l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
133 l_has_string?("label_#{s}".to_sym) ? l("label_#{s}".to_sym) : s.to_s.humanize
134 end
134 end
135
135
136 def day_name(day)
136 def day_name(day)
137 l(:general_day_names).split(',')[day-1]
137 l(:general_day_names).split(',')[day-1]
138 end
138 end
139
139
140 def month_name(month)
140 def month_name(month)
141 l(:actionview_datehelper_select_month_names).split(',')[month-1]
141 l(:actionview_datehelper_select_month_names).split(',')[month-1]
142 end
142 end
143
143
144 def syntax_highlight(name, content)
144 def syntax_highlight(name, content)
145 type = CodeRay::FileType[name]
145 type = CodeRay::FileType[name]
146 type ? CodeRay.scan(content, type).html : h(content)
146 type ? CodeRay.scan(content, type).html : h(content)
147 end
147 end
148
148
149 def to_path_param(path)
149 def to_path_param(path)
150 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
150 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
151 end
151 end
152
152
153 def pagination_links_full(paginator, count=nil, options={})
153 def pagination_links_full(paginator, count=nil, options={})
154 page_param = options.delete(:page_param) || :page
154 page_param = options.delete(:page_param) || :page
155 url_param = params.dup
155 url_param = params.dup
156 # don't reuse params if filters are present
156 # don't reuse params if filters are present
157 url_param.clear if url_param.has_key?(:set_filter)
157 url_param.clear if url_param.has_key?(:set_filter)
158
158
159 html = ''
159 html = ''
160 html << link_to_remote(('&#171; ' + l(:label_previous)),
160 html << link_to_remote(('&#171; ' + l(:label_previous)),
161 {:update => 'content',
161 {:update => 'content',
162 :url => url_param.merge(page_param => paginator.current.previous),
162 :url => url_param.merge(page_param => paginator.current.previous),
163 :complete => 'window.scrollTo(0,0)'},
163 :complete => 'window.scrollTo(0,0)'},
164 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
164 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
165
165
166 html << (pagination_links_each(paginator, options) do |n|
166 html << (pagination_links_each(paginator, options) do |n|
167 link_to_remote(n.to_s,
167 link_to_remote(n.to_s,
168 {:url => {:params => url_param.merge(page_param => n)},
168 {:url => {:params => url_param.merge(page_param => n)},
169 :update => 'content',
169 :update => 'content',
170 :complete => 'window.scrollTo(0,0)'},
170 :complete => 'window.scrollTo(0,0)'},
171 {:href => url_for(:params => url_param.merge(page_param => n))})
171 {:href => url_for(:params => url_param.merge(page_param => n))})
172 end || '')
172 end || '')
173
173
174 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
174 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
175 {:update => 'content',
175 {:update => 'content',
176 :url => url_param.merge(page_param => paginator.current.next),
176 :url => url_param.merge(page_param => paginator.current.next),
177 :complete => 'window.scrollTo(0,0)'},
177 :complete => 'window.scrollTo(0,0)'},
178 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
178 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
179
179
180 unless count.nil?
180 unless count.nil?
181 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
181 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
182 end
182 end
183
183
184 html
184 html
185 end
185 end
186
186
187 def per_page_links(selected=nil)
187 def per_page_links(selected=nil)
188 url_param = params.dup
188 url_param = params.dup
189 url_param.clear if url_param.has_key?(:set_filter)
189 url_param.clear if url_param.has_key?(:set_filter)
190
190
191 links = Setting.per_page_options_array.collect do |n|
191 links = Setting.per_page_options_array.collect do |n|
192 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
192 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
193 {:href => url_for(url_param.merge(:per_page => n))})
193 {:href => url_for(url_param.merge(:per_page => n))})
194 end
194 end
195 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
195 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
196 end
196 end
197
197
198 def breadcrumb(*args)
198 def breadcrumb(*args)
199 elements = args.flatten
199 elements = args.flatten
200 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
200 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
201 end
201 end
202
202
203 def html_title(*args)
203 def html_title(*args)
204 if args.empty?
204 if args.empty?
205 title = []
205 title = []
206 title << @project.name if @project
206 title << @project.name if @project
207 title += @html_title if @html_title
207 title += @html_title if @html_title
208 title << Setting.app_title
208 title << Setting.app_title
209 title.compact.join(' - ')
209 title.compact.join(' - ')
210 else
210 else
211 @html_title ||= []
211 @html_title ||= []
212 @html_title += args
212 @html_title += args
213 end
213 end
214 end
214 end
215
215
216 def accesskey(s)
216 def accesskey(s)
217 Redmine::AccessKeys.key_for s
217 Redmine::AccessKeys.key_for s
218 end
218 end
219
219
220 # Formats text according to system settings.
220 # Formats text according to system settings.
221 # 2 ways to call this method:
221 # 2 ways to call this method:
222 # * with a String: textilizable(text, options)
222 # * with a String: textilizable(text, options)
223 # * with an object and one of its attribute: textilizable(issue, :description, options)
223 # * with an object and one of its attribute: textilizable(issue, :description, options)
224 def textilizable(*args)
224 def textilizable(*args)
225 options = args.last.is_a?(Hash) ? args.pop : {}
225 options = args.last.is_a?(Hash) ? args.pop : {}
226 case args.size
226 case args.size
227 when 1
227 when 1
228 obj = options[:object]
228 obj = options[:object]
229 text = args.shift
229 text = args.shift
230 when 2
230 when 2
231 obj = args.shift
231 obj = args.shift
232 text = obj.send(args.shift).to_s
232 text = obj.send(args.shift).to_s
233 else
233 else
234 raise ArgumentError, 'invalid arguments to textilizable'
234 raise ArgumentError, 'invalid arguments to textilizable'
235 end
235 end
236 return '' if text.blank?
236 return '' if text.blank?
237
237
238 only_path = options.delete(:only_path) == false ? false : true
238 only_path = options.delete(:only_path) == false ? false : true
239
239
240 # when using an image link, try to use an attachment, if possible
240 # when using an image link, try to use an attachment, if possible
241 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
241 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
242
242
243 if attachments
243 if attachments
244 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
244 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
245 style = $1
245 style = $1
246 filename = $6
246 filename = $6
247 rf = Regexp.new(filename, Regexp::IGNORECASE)
247 rf = Regexp.new(Regexp.escape(filename), Regexp::IGNORECASE)
248 # search for the picture in attachments
248 # search for the picture in attachments
249 if found = attachments.detect { |att| att.filename =~ rf }
249 if found = attachments.detect { |att| att.filename =~ rf }
250 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
250 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
251 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
251 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
252 alt = desc.blank? ? nil : "(#{desc})"
252 alt = desc.blank? ? nil : "(#{desc})"
253 "!#{style}#{image_url}#{alt}!"
253 "!#{style}#{image_url}#{alt}!"
254 else
254 else
255 "!#{style}#{filename}!"
255 "!#{style}#{filename}!"
256 end
256 end
257 end
257 end
258 end
258 end
259
259
260 text = (Setting.text_formatting == 'textile') ?
260 text = (Setting.text_formatting == 'textile') ?
261 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
261 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
262 simple_format(auto_link(h(text)))
262 simple_format(auto_link(h(text)))
263
263
264 # different methods for formatting wiki links
264 # different methods for formatting wiki links
265 case options[:wiki_links]
265 case options[:wiki_links]
266 when :local
266 when :local
267 # used for local links to html files
267 # used for local links to html files
268 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
268 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
269 when :anchor
269 when :anchor
270 # used for single-file wiki export
270 # used for single-file wiki export
271 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
271 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
272 else
272 else
273 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
273 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
274 end
274 end
275
275
276 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
276 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
277
277
278 # Wiki links
278 # Wiki links
279 #
279 #
280 # Examples:
280 # Examples:
281 # [[mypage]]
281 # [[mypage]]
282 # [[mypage|mytext]]
282 # [[mypage|mytext]]
283 # wiki links can refer other project wikis, using project name or identifier:
283 # wiki links can refer other project wikis, using project name or identifier:
284 # [[project:]] -> wiki starting page
284 # [[project:]] -> wiki starting page
285 # [[project:|mytext]]
285 # [[project:|mytext]]
286 # [[project:mypage]]
286 # [[project:mypage]]
287 # [[project:mypage|mytext]]
287 # [[project:mypage|mytext]]
288 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
288 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
289 link_project = project
289 link_project = project
290 esc, all, page, title = $1, $2, $3, $5
290 esc, all, page, title = $1, $2, $3, $5
291 if esc.nil?
291 if esc.nil?
292 if page =~ /^([^\:]+)\:(.*)$/
292 if page =~ /^([^\:]+)\:(.*)$/
293 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
293 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
294 page = $2
294 page = $2
295 title ||= $1 if page.blank?
295 title ||= $1 if page.blank?
296 end
296 end
297
297
298 if link_project && link_project.wiki
298 if link_project && link_project.wiki
299 # extract anchor
299 # extract anchor
300 anchor = nil
300 anchor = nil
301 if page =~ /^(.+?)\#(.+)$/
301 if page =~ /^(.+?)\#(.+)$/
302 page, anchor = $1, $2
302 page, anchor = $1, $2
303 end
303 end
304 # check if page exists
304 # check if page exists
305 wiki_page = link_project.wiki.find_page(page)
305 wiki_page = link_project.wiki.find_page(page)
306 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
306 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
307 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
307 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
308 else
308 else
309 # project or wiki doesn't exist
309 # project or wiki doesn't exist
310 title || page
310 title || page
311 end
311 end
312 else
312 else
313 all
313 all
314 end
314 end
315 end
315 end
316
316
317 # Redmine links
317 # Redmine links
318 #
318 #
319 # Examples:
319 # Examples:
320 # Issues:
320 # Issues:
321 # #52 -> Link to issue #52
321 # #52 -> Link to issue #52
322 # Changesets:
322 # Changesets:
323 # r52 -> Link to revision 52
323 # r52 -> Link to revision 52
324 # commit:a85130f -> Link to scmid starting with a85130f
324 # commit:a85130f -> Link to scmid starting with a85130f
325 # Documents:
325 # Documents:
326 # document#17 -> Link to document with id 17
326 # document#17 -> Link to document with id 17
327 # document:Greetings -> Link to the document with title "Greetings"
327 # document:Greetings -> Link to the document with title "Greetings"
328 # document:"Some document" -> Link to the document with title "Some document"
328 # document:"Some document" -> Link to the document with title "Some document"
329 # Versions:
329 # Versions:
330 # version#3 -> Link to version with id 3
330 # version#3 -> Link to version with id 3
331 # version:1.0.0 -> Link to version named "1.0.0"
331 # version:1.0.0 -> Link to version named "1.0.0"
332 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
332 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
333 # Attachments:
333 # Attachments:
334 # attachment:file.zip -> Link to the attachment of the current object named file.zip
334 # attachment:file.zip -> Link to the attachment of the current object named file.zip
335 # Source files:
335 # Source files:
336 # source:some/file -> Link to the file located at /some/file in the project's repository
336 # source:some/file -> Link to the file located at /some/file in the project's repository
337 # source:some/file@52 -> Link to the file's revision 52
337 # source:some/file@52 -> Link to the file's revision 52
338 # source:some/file#L120 -> Link to line 120 of the file
338 # source:some/file#L120 -> Link to line 120 of the file
339 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
339 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
340 # export:some/file -> Force the download of the file
340 # export:some/file -> Force the download of the file
341 # Forum messages:
341 # Forum messages:
342 # message#1218 -> Link to message with id 1218
342 # message#1218 -> Link to message with id 1218
343 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|\s|<|$)}) do |m|
343 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|\s|<|$)}) do |m|
344 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
344 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
345 link = nil
345 link = nil
346 if esc.nil?
346 if esc.nil?
347 if prefix.nil? && sep == 'r'
347 if prefix.nil? && sep == 'r'
348 if project && (changeset = project.changesets.find_by_revision(oid))
348 if project && (changeset = project.changesets.find_by_revision(oid))
349 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
349 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
350 :class => 'changeset',
350 :class => 'changeset',
351 :title => truncate_single_line(changeset.comments, 100))
351 :title => truncate_single_line(changeset.comments, 100))
352 end
352 end
353 elsif sep == '#'
353 elsif sep == '#'
354 oid = oid.to_i
354 oid = oid.to_i
355 case prefix
355 case prefix
356 when nil
356 when nil
357 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
357 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
358 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
358 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
359 :class => (issue.closed? ? 'issue closed' : 'issue'),
359 :class => (issue.closed? ? 'issue closed' : 'issue'),
360 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
360 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
361 link = content_tag('del', link) if issue.closed?
361 link = content_tag('del', link) if issue.closed?
362 end
362 end
363 when 'document'
363 when 'document'
364 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
364 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
365 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
365 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
366 :class => 'document'
366 :class => 'document'
367 end
367 end
368 when 'version'
368 when 'version'
369 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
369 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
370 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
370 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
371 :class => 'version'
371 :class => 'version'
372 end
372 end
373 when 'message'
373 when 'message'
374 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
374 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
375 link = link_to h(truncate(message.subject, 60)), {:only_path => only_path,
375 link = link_to h(truncate(message.subject, 60)), {:only_path => only_path,
376 :controller => 'messages',
376 :controller => 'messages',
377 :action => 'show',
377 :action => 'show',
378 :board_id => message.board,
378 :board_id => message.board,
379 :id => message.root,
379 :id => message.root,
380 :anchor => (message.parent ? "message-#{message.id}" : nil)},
380 :anchor => (message.parent ? "message-#{message.id}" : nil)},
381 :class => 'message'
381 :class => 'message'
382 end
382 end
383 end
383 end
384 elsif sep == ':'
384 elsif sep == ':'
385 # removes the double quotes if any
385 # removes the double quotes if any
386 name = oid.gsub(%r{^"(.*)"$}, "\\1")
386 name = oid.gsub(%r{^"(.*)"$}, "\\1")
387 case prefix
387 case prefix
388 when 'document'
388 when 'document'
389 if project && document = project.documents.find_by_title(name)
389 if project && document = project.documents.find_by_title(name)
390 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
390 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
391 :class => 'document'
391 :class => 'document'
392 end
392 end
393 when 'version'
393 when 'version'
394 if project && version = project.versions.find_by_name(name)
394 if project && version = project.versions.find_by_name(name)
395 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
395 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
396 :class => 'version'
396 :class => 'version'
397 end
397 end
398 when 'commit'
398 when 'commit'
399 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
399 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
400 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
400 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
401 :class => 'changeset',
401 :class => 'changeset',
402 :title => truncate_single_line(changeset.comments, 100)
402 :title => truncate_single_line(changeset.comments, 100)
403 end
403 end
404 when 'source', 'export'
404 when 'source', 'export'
405 if project && project.repository
405 if project && project.repository
406 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
406 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
407 path, rev, anchor = $1, $3, $5
407 path, rev, anchor = $1, $3, $5
408 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
408 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
409 :path => to_path_param(path),
409 :path => to_path_param(path),
410 :rev => rev,
410 :rev => rev,
411 :anchor => anchor,
411 :anchor => anchor,
412 :format => (prefix == 'export' ? 'raw' : nil)},
412 :format => (prefix == 'export' ? 'raw' : nil)},
413 :class => (prefix == 'export' ? 'source download' : 'source')
413 :class => (prefix == 'export' ? 'source download' : 'source')
414 end
414 end
415 when 'attachment'
415 when 'attachment'
416 if attachments && attachment = attachments.detect {|a| a.filename == name }
416 if attachments && attachment = attachments.detect {|a| a.filename == name }
417 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
417 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
418 :class => 'attachment'
418 :class => 'attachment'
419 end
419 end
420 end
420 end
421 end
421 end
422 end
422 end
423 leading + (link || "#{prefix}#{sep}#{oid}")
423 leading + (link || "#{prefix}#{sep}#{oid}")
424 end
424 end
425
425
426 text
426 text
427 end
427 end
428
428
429 # Same as Rails' simple_format helper without using paragraphs
429 # Same as Rails' simple_format helper without using paragraphs
430 def simple_format_without_paragraph(text)
430 def simple_format_without_paragraph(text)
431 text.to_s.
431 text.to_s.
432 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
432 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
433 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
433 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
434 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
434 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
435 end
435 end
436
436
437 def error_messages_for(object_name, options = {})
437 def error_messages_for(object_name, options = {})
438 options = options.symbolize_keys
438 options = options.symbolize_keys
439 object = instance_variable_get("@#{object_name}")
439 object = instance_variable_get("@#{object_name}")
440 if object && !object.errors.empty?
440 if object && !object.errors.empty?
441 # build full_messages here with controller current language
441 # build full_messages here with controller current language
442 full_messages = []
442 full_messages = []
443 object.errors.each do |attr, msg|
443 object.errors.each do |attr, msg|
444 next if msg.nil?
444 next if msg.nil?
445 msg = msg.first if msg.is_a? Array
445 msg = msg.first if msg.is_a? Array
446 if attr == "base"
446 if attr == "base"
447 full_messages << l(msg)
447 full_messages << l(msg)
448 else
448 else
449 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
449 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
450 end
450 end
451 end
451 end
452 # retrieve custom values error messages
452 # retrieve custom values error messages
453 if object.errors[:custom_values]
453 if object.errors[:custom_values]
454 object.custom_values.each do |v|
454 object.custom_values.each do |v|
455 v.errors.each do |attr, msg|
455 v.errors.each do |attr, msg|
456 next if msg.nil?
456 next if msg.nil?
457 msg = msg.first if msg.is_a? Array
457 msg = msg.first if msg.is_a? Array
458 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
458 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
459 end
459 end
460 end
460 end
461 end
461 end
462 content_tag("div",
462 content_tag("div",
463 content_tag(
463 content_tag(
464 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
464 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
465 ) +
465 ) +
466 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
466 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
467 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
467 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
468 )
468 )
469 else
469 else
470 ""
470 ""
471 end
471 end
472 end
472 end
473
473
474 def lang_options_for_select(blank=true)
474 def lang_options_for_select(blank=true)
475 (blank ? [["(auto)", ""]] : []) +
475 (blank ? [["(auto)", ""]] : []) +
476 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
476 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
477 end
477 end
478
478
479 def label_tag_for(name, option_tags = nil, options = {})
479 def label_tag_for(name, option_tags = nil, options = {})
480 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
480 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
481 content_tag("label", label_text)
481 content_tag("label", label_text)
482 end
482 end
483
483
484 def labelled_tabular_form_for(name, object, options, &proc)
484 def labelled_tabular_form_for(name, object, options, &proc)
485 options[:html] ||= {}
485 options[:html] ||= {}
486 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
486 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
487 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
487 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
488 end
488 end
489
489
490 def back_url_hidden_field_tag
490 def back_url_hidden_field_tag
491 back_url = params[:back_url] || request.env['HTTP_REFERER']
491 back_url = params[:back_url] || request.env['HTTP_REFERER']
492 hidden_field_tag('back_url', back_url) unless back_url.blank?
492 hidden_field_tag('back_url', back_url) unless back_url.blank?
493 end
493 end
494
494
495 def check_all_links(form_name)
495 def check_all_links(form_name)
496 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
496 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
497 " | " +
497 " | " +
498 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
498 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
499 end
499 end
500
500
501 def progress_bar(pcts, options={})
501 def progress_bar(pcts, options={})
502 pcts = [pcts, pcts] unless pcts.is_a?(Array)
502 pcts = [pcts, pcts] unless pcts.is_a?(Array)
503 pcts[1] = pcts[1] - pcts[0]
503 pcts[1] = pcts[1] - pcts[0]
504 pcts << (100 - pcts[1] - pcts[0])
504 pcts << (100 - pcts[1] - pcts[0])
505 width = options[:width] || '100px;'
505 width = options[:width] || '100px;'
506 legend = options[:legend] || ''
506 legend = options[:legend] || ''
507 content_tag('table',
507 content_tag('table',
508 content_tag('tr',
508 content_tag('tr',
509 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
509 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
510 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
510 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
511 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
511 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
512 ), :class => 'progress', :style => "width: #{width};") +
512 ), :class => 'progress', :style => "width: #{width};") +
513 content_tag('p', legend, :class => 'pourcent')
513 content_tag('p', legend, :class => 'pourcent')
514 end
514 end
515
515
516 def context_menu_link(name, url, options={})
516 def context_menu_link(name, url, options={})
517 options[:class] ||= ''
517 options[:class] ||= ''
518 if options.delete(:selected)
518 if options.delete(:selected)
519 options[:class] << ' icon-checked disabled'
519 options[:class] << ' icon-checked disabled'
520 options[:disabled] = true
520 options[:disabled] = true
521 end
521 end
522 if options.delete(:disabled)
522 if options.delete(:disabled)
523 options.delete(:method)
523 options.delete(:method)
524 options.delete(:confirm)
524 options.delete(:confirm)
525 options.delete(:onclick)
525 options.delete(:onclick)
526 options[:class] << ' disabled'
526 options[:class] << ' disabled'
527 url = '#'
527 url = '#'
528 end
528 end
529 link_to name, url, options
529 link_to name, url, options
530 end
530 end
531
531
532 def calendar_for(field_id)
532 def calendar_for(field_id)
533 include_calendar_headers_tags
533 include_calendar_headers_tags
534 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
534 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
535 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
535 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
536 end
536 end
537
537
538 def include_calendar_headers_tags
538 def include_calendar_headers_tags
539 unless @calendar_headers_tags_included
539 unless @calendar_headers_tags_included
540 @calendar_headers_tags_included = true
540 @calendar_headers_tags_included = true
541 content_for :header_tags do
541 content_for :header_tags do
542 javascript_include_tag('calendar/calendar') +
542 javascript_include_tag('calendar/calendar') +
543 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
543 javascript_include_tag("calendar/lang/calendar-#{current_language}.js") +
544 javascript_include_tag('calendar/calendar-setup') +
544 javascript_include_tag('calendar/calendar-setup') +
545 stylesheet_link_tag('calendar')
545 stylesheet_link_tag('calendar')
546 end
546 end
547 end
547 end
548 end
548 end
549
549
550 def wikitoolbar_for(field_id)
550 def wikitoolbar_for(field_id)
551 return '' unless Setting.text_formatting == 'textile'
551 return '' unless Setting.text_formatting == 'textile'
552
552
553 help_link = l(:setting_text_formatting) + ': ' +
553 help_link = l(:setting_text_formatting) + ': ' +
554 link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
554 link_to(l(:label_help), compute_public_path('wiki_syntax', 'help', 'html'),
555 :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;")
555 :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;")
556
556
557 javascript_include_tag('jstoolbar/jstoolbar') +
557 javascript_include_tag('jstoolbar/jstoolbar') +
558 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
558 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
559 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
559 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.setHelpLink('#{help_link}'); toolbar.draw();")
560 end
560 end
561
561
562 def content_for(name, content = nil, &block)
562 def content_for(name, content = nil, &block)
563 @has_content ||= {}
563 @has_content ||= {}
564 @has_content[name] = true
564 @has_content[name] = true
565 super(name, content, &block)
565 super(name, content, &block)
566 end
566 end
567
567
568 def has_content?(name)
568 def has_content?(name)
569 (@has_content && @has_content[name]) || false
569 (@has_content && @has_content[name]) || false
570 end
570 end
571 end
571 end
General Comments 0
You need to be logged in to leave comments. Login now