##// END OF EJS Templates
Rails3: helper: use html_safe at page_header_title of ApplicationHelper...
Toshi MARUYAMA -
r7467:c27328236293
parent child
Show More
@@ -1,986 +1,986
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 'forwardable'
18 require 'forwardable'
19 require 'cgi'
19 require 'cgi'
20
20
21 module ApplicationHelper
21 module ApplicationHelper
22 include Redmine::WikiFormatting::Macros::Definitions
22 include Redmine::WikiFormatting::Macros::Definitions
23 include Redmine::I18n
23 include Redmine::I18n
24 include GravatarHelper::PublicMethods
24 include GravatarHelper::PublicMethods
25
25
26 extend Forwardable
26 extend Forwardable
27 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
27 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
28
28
29 # Return true if user is authorized for controller/action, otherwise false
29 # Return true if user is authorized for controller/action, otherwise false
30 def authorize_for(controller, action)
30 def authorize_for(controller, action)
31 User.current.allowed_to?({:controller => controller, :action => action}, @project)
31 User.current.allowed_to?({:controller => controller, :action => action}, @project)
32 end
32 end
33
33
34 # Display a link if user is authorized
34 # Display a link if user is authorized
35 #
35 #
36 # @param [String] name Anchor text (passed to link_to)
36 # @param [String] name Anchor text (passed to link_to)
37 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
37 # @param [Hash] options Hash params. This will checked by authorize_for to see if the user is authorized
38 # @param [optional, Hash] html_options Options passed to link_to
38 # @param [optional, Hash] html_options Options passed to link_to
39 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
39 # @param [optional, Hash] parameters_for_method_reference Extra parameters for link_to
40 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
40 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
41 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
41 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
42 end
42 end
43
43
44 # Display a link to remote if user is authorized
44 # Display a link to remote if user is authorized
45 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
45 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
46 url = options[:url] || {}
46 url = options[:url] || {}
47 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
47 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
48 end
48 end
49
49
50 # Displays a link to user's account page if active
50 # Displays a link to user's account page if active
51 def link_to_user(user, options={})
51 def link_to_user(user, options={})
52 if user.is_a?(User)
52 if user.is_a?(User)
53 name = h(user.name(options[:format]))
53 name = h(user.name(options[:format]))
54 if user.active?
54 if user.active?
55 link_to name, :controller => 'users', :action => 'show', :id => user
55 link_to name, :controller => 'users', :action => 'show', :id => user
56 else
56 else
57 name
57 name
58 end
58 end
59 else
59 else
60 h(user.to_s)
60 h(user.to_s)
61 end
61 end
62 end
62 end
63
63
64 # Displays a link to +issue+ with its subject.
64 # Displays a link to +issue+ with its subject.
65 # Examples:
65 # Examples:
66 #
66 #
67 # link_to_issue(issue) # => Defect #6: This is the subject
67 # link_to_issue(issue) # => Defect #6: This is the subject
68 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
68 # link_to_issue(issue, :truncate => 6) # => Defect #6: This i...
69 # link_to_issue(issue, :subject => false) # => Defect #6
69 # link_to_issue(issue, :subject => false) # => Defect #6
70 # link_to_issue(issue, :project => true) # => Foo - Defect #6
70 # link_to_issue(issue, :project => true) # => Foo - Defect #6
71 #
71 #
72 def link_to_issue(issue, options={})
72 def link_to_issue(issue, options={})
73 title = nil
73 title = nil
74 subject = nil
74 subject = nil
75 if options[:subject] == false
75 if options[:subject] == false
76 title = truncate(issue.subject, :length => 60)
76 title = truncate(issue.subject, :length => 60)
77 else
77 else
78 subject = issue.subject
78 subject = issue.subject
79 if options[:truncate]
79 if options[:truncate]
80 subject = truncate(subject, :length => options[:truncate])
80 subject = truncate(subject, :length => options[:truncate])
81 end
81 end
82 end
82 end
83 s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
83 s = link_to "#{h(issue.tracker)} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue},
84 :class => issue.css_classes,
84 :class => issue.css_classes,
85 :title => title
85 :title => title
86 s << ": #{h subject}" if subject
86 s << ": #{h subject}" if subject
87 s = "#{h issue.project} - " + s if options[:project]
87 s = "#{h issue.project} - " + s if options[:project]
88 s
88 s
89 end
89 end
90
90
91 # Generates a link to an attachment.
91 # Generates a link to an attachment.
92 # Options:
92 # Options:
93 # * :text - Link text (default to attachment filename)
93 # * :text - Link text (default to attachment filename)
94 # * :download - Force download (default: false)
94 # * :download - Force download (default: false)
95 def link_to_attachment(attachment, options={})
95 def link_to_attachment(attachment, options={})
96 text = options.delete(:text) || attachment.filename
96 text = options.delete(:text) || attachment.filename
97 action = options.delete(:download) ? 'download' : 'show'
97 action = options.delete(:download) ? 'download' : 'show'
98
98
99 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
99 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
100 end
100 end
101
101
102 # Generates a link to a SCM revision
102 # Generates a link to a SCM revision
103 # Options:
103 # Options:
104 # * :text - Link text (default to the formatted revision)
104 # * :text - Link text (default to the formatted revision)
105 def link_to_revision(revision, project, options={})
105 def link_to_revision(revision, project, options={})
106 text = options.delete(:text) || format_revision(revision)
106 text = options.delete(:text) || format_revision(revision)
107 rev = revision.respond_to?(:identifier) ? revision.identifier : revision
107 rev = revision.respond_to?(:identifier) ? revision.identifier : revision
108
108
109 link_to(h(text), {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
109 link_to(h(text), {:controller => 'repositories', :action => 'revision', :id => project, :rev => rev},
110 :title => l(:label_revision_id, format_revision(revision)))
110 :title => l(:label_revision_id, format_revision(revision)))
111 end
111 end
112
112
113 # Generates a link to a message
113 # Generates a link to a message
114 def link_to_message(message, options={}, html_options = nil)
114 def link_to_message(message, options={}, html_options = nil)
115 link_to(
115 link_to(
116 h(truncate(message.subject, :length => 60)),
116 h(truncate(message.subject, :length => 60)),
117 { :controller => 'messages', :action => 'show',
117 { :controller => 'messages', :action => 'show',
118 :board_id => message.board_id,
118 :board_id => message.board_id,
119 :id => message.root,
119 :id => message.root,
120 :r => (message.parent_id && message.id),
120 :r => (message.parent_id && message.id),
121 :anchor => (message.parent_id ? "message-#{message.id}" : nil)
121 :anchor => (message.parent_id ? "message-#{message.id}" : nil)
122 }.merge(options),
122 }.merge(options),
123 html_options
123 html_options
124 )
124 )
125 end
125 end
126
126
127 # Generates a link to a project if active
127 # Generates a link to a project if active
128 # Examples:
128 # Examples:
129 #
129 #
130 # link_to_project(project) # => link to the specified project overview
130 # link_to_project(project) # => link to the specified project overview
131 # link_to_project(project, :action=>'settings') # => link to project settings
131 # link_to_project(project, :action=>'settings') # => link to project settings
132 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
132 # link_to_project(project, {:only_path => false}, :class => "project") # => 3rd arg adds html options
133 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
133 # link_to_project(project, {}, :class => "project") # => html options with default url (project overview)
134 #
134 #
135 def link_to_project(project, options={}, html_options = nil)
135 def link_to_project(project, options={}, html_options = nil)
136 if project.active?
136 if project.active?
137 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
137 url = {:controller => 'projects', :action => 'show', :id => project}.merge(options)
138 link_to(h(project), url, html_options)
138 link_to(h(project), url, html_options)
139 else
139 else
140 h(project)
140 h(project)
141 end
141 end
142 end
142 end
143
143
144 def toggle_link(name, id, options={})
144 def toggle_link(name, id, options={})
145 onclick = "Element.toggle('#{id}'); "
145 onclick = "Element.toggle('#{id}'); "
146 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
146 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
147 onclick << "return false;"
147 onclick << "return false;"
148 link_to(name, "#", :onclick => onclick)
148 link_to(name, "#", :onclick => onclick)
149 end
149 end
150
150
151 def image_to_function(name, function, html_options = {})
151 def image_to_function(name, function, html_options = {})
152 html_options.symbolize_keys!
152 html_options.symbolize_keys!
153 tag(:input, html_options.merge({
153 tag(:input, html_options.merge({
154 :type => "image", :src => image_path(name),
154 :type => "image", :src => image_path(name),
155 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
155 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
156 }))
156 }))
157 end
157 end
158
158
159 def prompt_to_remote(name, text, param, url, html_options = {})
159 def prompt_to_remote(name, text, param, url, html_options = {})
160 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
160 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
161 link_to name, {}, html_options
161 link_to name, {}, html_options
162 end
162 end
163
163
164 def format_activity_title(text)
164 def format_activity_title(text)
165 h(truncate_single_line(text, :length => 100))
165 h(truncate_single_line(text, :length => 100))
166 end
166 end
167
167
168 def format_activity_day(date)
168 def format_activity_day(date)
169 date == Date.today ? l(:label_today).titleize : format_date(date)
169 date == Date.today ? l(:label_today).titleize : format_date(date)
170 end
170 end
171
171
172 def format_activity_description(text)
172 def format_activity_description(text)
173 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
173 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
174 end
174 end
175
175
176 def format_version_name(version)
176 def format_version_name(version)
177 if version.project == @project
177 if version.project == @project
178 h(version)
178 h(version)
179 else
179 else
180 h("#{version.project} - #{version}")
180 h("#{version.project} - #{version}")
181 end
181 end
182 end
182 end
183
183
184 def due_date_distance_in_words(date)
184 def due_date_distance_in_words(date)
185 if date
185 if date
186 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
186 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
187 end
187 end
188 end
188 end
189
189
190 def render_page_hierarchy(pages, node=nil, options={})
190 def render_page_hierarchy(pages, node=nil, options={})
191 content = ''
191 content = ''
192 if pages[node]
192 if pages[node]
193 content << "<ul class=\"pages-hierarchy\">\n"
193 content << "<ul class=\"pages-hierarchy\">\n"
194 pages[node].each do |page|
194 pages[node].each do |page|
195 content << "<li>"
195 content << "<li>"
196 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
196 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'show', :project_id => page.project, :id => page.title},
197 :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
197 :title => (options[:timestamp] && page.updated_on ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
198 content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
198 content << "\n" + render_page_hierarchy(pages, page.id, options) if pages[page.id]
199 content << "</li>\n"
199 content << "</li>\n"
200 end
200 end
201 content << "</ul>\n"
201 content << "</ul>\n"
202 end
202 end
203 content.html_safe
203 content.html_safe
204 end
204 end
205
205
206 # Renders flash messages
206 # Renders flash messages
207 def render_flash_messages
207 def render_flash_messages
208 s = ''
208 s = ''
209 flash.each do |k,v|
209 flash.each do |k,v|
210 s << content_tag('div', v, :class => "flash #{k}")
210 s << content_tag('div', v, :class => "flash #{k}")
211 end
211 end
212 s.html_safe
212 s.html_safe
213 end
213 end
214
214
215 # Renders tabs and their content
215 # Renders tabs and their content
216 def render_tabs(tabs)
216 def render_tabs(tabs)
217 if tabs.any?
217 if tabs.any?
218 render :partial => 'common/tabs', :locals => {:tabs => tabs}
218 render :partial => 'common/tabs', :locals => {:tabs => tabs}
219 else
219 else
220 content_tag 'p', l(:label_no_data), :class => "nodata"
220 content_tag 'p', l(:label_no_data), :class => "nodata"
221 end
221 end
222 end
222 end
223
223
224 # Renders the project quick-jump box
224 # Renders the project quick-jump box
225 def render_project_jump_box
225 def render_project_jump_box
226 return unless User.current.logged?
226 return unless User.current.logged?
227 projects = User.current.memberships.collect(&:project).compact.uniq
227 projects = User.current.memberships.collect(&:project).compact.uniq
228 if projects.any?
228 if projects.any?
229 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
229 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
230 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
230 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
231 '<option value="" disabled="disabled">---</option>'
231 '<option value="" disabled="disabled">---</option>'
232 s << project_tree_options_for_select(projects, :selected => @project) do |p|
232 s << project_tree_options_for_select(projects, :selected => @project) do |p|
233 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
233 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
234 end
234 end
235 s << '</select>'
235 s << '</select>'
236 s.html_safe
236 s.html_safe
237 end
237 end
238 end
238 end
239
239
240 def project_tree_options_for_select(projects, options = {})
240 def project_tree_options_for_select(projects, options = {})
241 s = ''
241 s = ''
242 project_tree(projects) do |project, level|
242 project_tree(projects) do |project, level|
243 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
243 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
244 tag_options = {:value => project.id}
244 tag_options = {:value => project.id}
245 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
245 if project == options[:selected] || (options[:selected].respond_to?(:include?) && options[:selected].include?(project))
246 tag_options[:selected] = 'selected'
246 tag_options[:selected] = 'selected'
247 else
247 else
248 tag_options[:selected] = nil
248 tag_options[:selected] = nil
249 end
249 end
250 tag_options.merge!(yield(project)) if block_given?
250 tag_options.merge!(yield(project)) if block_given?
251 s << content_tag('option', name_prefix + h(project), tag_options)
251 s << content_tag('option', name_prefix + h(project), tag_options)
252 end
252 end
253 s.html_safe
253 s.html_safe
254 end
254 end
255
255
256 # Yields the given block for each project with its level in the tree
256 # Yields the given block for each project with its level in the tree
257 #
257 #
258 # Wrapper for Project#project_tree
258 # Wrapper for Project#project_tree
259 def project_tree(projects, &block)
259 def project_tree(projects, &block)
260 Project.project_tree(projects, &block)
260 Project.project_tree(projects, &block)
261 end
261 end
262
262
263 def project_nested_ul(projects, &block)
263 def project_nested_ul(projects, &block)
264 s = ''
264 s = ''
265 if projects.any?
265 if projects.any?
266 ancestors = []
266 ancestors = []
267 projects.sort_by(&:lft).each do |project|
267 projects.sort_by(&:lft).each do |project|
268 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
268 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
269 s << "<ul>\n"
269 s << "<ul>\n"
270 else
270 else
271 ancestors.pop
271 ancestors.pop
272 s << "</li>"
272 s << "</li>"
273 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
273 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
274 ancestors.pop
274 ancestors.pop
275 s << "</ul></li>\n"
275 s << "</ul></li>\n"
276 end
276 end
277 end
277 end
278 s << "<li>"
278 s << "<li>"
279 s << yield(project).to_s
279 s << yield(project).to_s
280 ancestors << project
280 ancestors << project
281 end
281 end
282 s << ("</li></ul>\n" * ancestors.size)
282 s << ("</li></ul>\n" * ancestors.size)
283 end
283 end
284 s.html_safe
284 s.html_safe
285 end
285 end
286
286
287 def principals_check_box_tags(name, principals)
287 def principals_check_box_tags(name, principals)
288 s = ''
288 s = ''
289 principals.sort.each do |principal|
289 principals.sort.each do |principal|
290 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
290 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
291 end
291 end
292 s.html_safe
292 s.html_safe
293 end
293 end
294
294
295 # Returns a string for users/groups option tags
295 # Returns a string for users/groups option tags
296 def principals_options_for_select(collection, selected=nil)
296 def principals_options_for_select(collection, selected=nil)
297 s = ''
297 s = ''
298 groups = ''
298 groups = ''
299 collection.sort.each do |element|
299 collection.sort.each do |element|
300 selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
300 selected_attribute = ' selected="selected"' if option_value_selected?(element, selected)
301 (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
301 (element.is_a?(Group) ? groups : s) << %(<option value="#{element.id}"#{selected_attribute}>#{h element.name}</option>)
302 end
302 end
303 unless groups.empty?
303 unless groups.empty?
304 s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
304 s << %(<optgroup label="#{h(l(:label_group_plural))}">#{groups}</optgroup>)
305 end
305 end
306 s
306 s
307 end
307 end
308
308
309 # Truncates and returns the string as a single line
309 # Truncates and returns the string as a single line
310 def truncate_single_line(string, *args)
310 def truncate_single_line(string, *args)
311 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
311 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
312 end
312 end
313
313
314 # Truncates at line break after 250 characters or options[:length]
314 # Truncates at line break after 250 characters or options[:length]
315 def truncate_lines(string, options={})
315 def truncate_lines(string, options={})
316 length = options[:length] || 250
316 length = options[:length] || 250
317 if string.to_s =~ /\A(.{#{length}}.*?)$/m
317 if string.to_s =~ /\A(.{#{length}}.*?)$/m
318 "#{$1}..."
318 "#{$1}..."
319 else
319 else
320 string
320 string
321 end
321 end
322 end
322 end
323
323
324 def html_hours(text)
324 def html_hours(text)
325 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
325 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>').html_safe
326 end
326 end
327
327
328 def authoring(created, author, options={})
328 def authoring(created, author, options={})
329 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
329 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created)).html_safe
330 end
330 end
331
331
332 def time_tag(time)
332 def time_tag(time)
333 text = distance_of_time_in_words(Time.now, time)
333 text = distance_of_time_in_words(Time.now, time)
334 if @project
334 if @project
335 link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
335 link_to(text, {:controller => 'activities', :action => 'index', :id => @project, :from => time.to_date}, :title => format_time(time))
336 else
336 else
337 content_tag('acronym', text, :title => format_time(time))
337 content_tag('acronym', text, :title => format_time(time))
338 end
338 end
339 end
339 end
340
340
341 def syntax_highlight(name, content)
341 def syntax_highlight(name, content)
342 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
342 Redmine::SyntaxHighlighting.highlight_by_filename(content, name)
343 end
343 end
344
344
345 def to_path_param(path)
345 def to_path_param(path)
346 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
346 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
347 end
347 end
348
348
349 def pagination_links_full(paginator, count=nil, options={})
349 def pagination_links_full(paginator, count=nil, options={})
350 page_param = options.delete(:page_param) || :page
350 page_param = options.delete(:page_param) || :page
351 per_page_links = options.delete(:per_page_links)
351 per_page_links = options.delete(:per_page_links)
352 url_param = params.dup
352 url_param = params.dup
353
353
354 html = ''
354 html = ''
355 if paginator.current.previous
355 if paginator.current.previous
356 # \xc2\xab(utf-8) = &#171;
356 # \xc2\xab(utf-8) = &#171;
357 html << link_to_content_update(
357 html << link_to_content_update(
358 "\xc2\xab " + l(:label_previous),
358 "\xc2\xab " + l(:label_previous),
359 url_param.merge(page_param => paginator.current.previous)) + ' '
359 url_param.merge(page_param => paginator.current.previous)) + ' '
360 end
360 end
361
361
362 html << (pagination_links_each(paginator, options) do |n|
362 html << (pagination_links_each(paginator, options) do |n|
363 link_to_content_update(n.to_s, url_param.merge(page_param => n))
363 link_to_content_update(n.to_s, url_param.merge(page_param => n))
364 end || '')
364 end || '')
365
365
366 if paginator.current.next
366 if paginator.current.next
367 # \xc2\xbb(utf-8) = &#187;
367 # \xc2\xbb(utf-8) = &#187;
368 html << ' ' + link_to_content_update(
368 html << ' ' + link_to_content_update(
369 (l(:label_next) + " \xc2\xbb"),
369 (l(:label_next) + " \xc2\xbb"),
370 url_param.merge(page_param => paginator.current.next))
370 url_param.merge(page_param => paginator.current.next))
371 end
371 end
372
372
373 unless count.nil?
373 unless count.nil?
374 html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
374 html << " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})"
375 if per_page_links != false && links = per_page_links(paginator.items_per_page)
375 if per_page_links != false && links = per_page_links(paginator.items_per_page)
376 html << " | #{links}"
376 html << " | #{links}"
377 end
377 end
378 end
378 end
379
379
380 html.html_safe
380 html.html_safe
381 end
381 end
382
382
383 def per_page_links(selected=nil)
383 def per_page_links(selected=nil)
384 links = Setting.per_page_options_array.collect do |n|
384 links = Setting.per_page_options_array.collect do |n|
385 n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
385 n == selected ? n : link_to_content_update(n, params.merge(:per_page => n))
386 end
386 end
387 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
387 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
388 end
388 end
389
389
390 def reorder_links(name, url)
390 def reorder_links(name, url)
391 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
391 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)),
392 url.merge({"#{name}[move_to]" => 'highest'}),
392 url.merge({"#{name}[move_to]" => 'highest'}),
393 :method => :post, :title => l(:label_sort_highest)) +
393 :method => :post, :title => l(:label_sort_highest)) +
394 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
394 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)),
395 url.merge({"#{name}[move_to]" => 'higher'}),
395 url.merge({"#{name}[move_to]" => 'higher'}),
396 :method => :post, :title => l(:label_sort_higher)) +
396 :method => :post, :title => l(:label_sort_higher)) +
397 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
397 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)),
398 url.merge({"#{name}[move_to]" => 'lower'}),
398 url.merge({"#{name}[move_to]" => 'lower'}),
399 :method => :post, :title => l(:label_sort_lower)) +
399 :method => :post, :title => l(:label_sort_lower)) +
400 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
400 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)),
401 url.merge({"#{name}[move_to]" => 'lowest'}),
401 url.merge({"#{name}[move_to]" => 'lowest'}),
402 :method => :post, :title => l(:label_sort_lowest))
402 :method => :post, :title => l(:label_sort_lowest))
403 end
403 end
404
404
405 def breadcrumb(*args)
405 def breadcrumb(*args)
406 elements = args.flatten
406 elements = args.flatten
407 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
407 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
408 end
408 end
409
409
410 def other_formats_links(&block)
410 def other_formats_links(&block)
411 concat('<p class="other-formats">' + l(:label_export_to))
411 concat('<p class="other-formats">' + l(:label_export_to))
412 yield Redmine::Views::OtherFormatsBuilder.new(self)
412 yield Redmine::Views::OtherFormatsBuilder.new(self)
413 concat('</p>')
413 concat('</p>')
414 end
414 end
415
415
416 def page_header_title
416 def page_header_title
417 if @project.nil? || @project.new_record?
417 if @project.nil? || @project.new_record?
418 h(Setting.app_title)
418 h(Setting.app_title)
419 else
419 else
420 b = []
420 b = []
421 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
421 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
422 if ancestors.any?
422 if ancestors.any?
423 root = ancestors.shift
423 root = ancestors.shift
424 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
424 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
425 if ancestors.size > 2
425 if ancestors.size > 2
426 b << "\xe2\x80\xa6"
426 b << "\xe2\x80\xa6"
427 ancestors = ancestors[-2, 2]
427 ancestors = ancestors[-2, 2]
428 end
428 end
429 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
429 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
430 end
430 end
431 b << h(@project)
431 b << h(@project)
432 b.join(" \xc2\xbb ")
432 b.join(" \xc2\xbb ").html_safe
433 end
433 end
434 end
434 end
435
435
436 def html_title(*args)
436 def html_title(*args)
437 if args.empty?
437 if args.empty?
438 title = []
438 title = []
439 title << @project.name if @project
439 title << @project.name if @project
440 title += @html_title if @html_title
440 title += @html_title if @html_title
441 title << Setting.app_title
441 title << Setting.app_title
442 title.select {|t| !t.blank? }.join(' - ')
442 title.select {|t| !t.blank? }.join(' - ')
443 else
443 else
444 @html_title ||= []
444 @html_title ||= []
445 @html_title += args
445 @html_title += args
446 end
446 end
447 end
447 end
448
448
449 # Returns the theme, controller name, and action as css classes for the
449 # Returns the theme, controller name, and action as css classes for the
450 # HTML body.
450 # HTML body.
451 def body_css_classes
451 def body_css_classes
452 css = []
452 css = []
453 if theme = Redmine::Themes.theme(Setting.ui_theme)
453 if theme = Redmine::Themes.theme(Setting.ui_theme)
454 css << 'theme-' + theme.name
454 css << 'theme-' + theme.name
455 end
455 end
456
456
457 css << 'controller-' + params[:controller]
457 css << 'controller-' + params[:controller]
458 css << 'action-' + params[:action]
458 css << 'action-' + params[:action]
459 css.join(' ')
459 css.join(' ')
460 end
460 end
461
461
462 def accesskey(s)
462 def accesskey(s)
463 Redmine::AccessKeys.key_for s
463 Redmine::AccessKeys.key_for s
464 end
464 end
465
465
466 # Formats text according to system settings.
466 # Formats text according to system settings.
467 # 2 ways to call this method:
467 # 2 ways to call this method:
468 # * with a String: textilizable(text, options)
468 # * with a String: textilizable(text, options)
469 # * with an object and one of its attribute: textilizable(issue, :description, options)
469 # * with an object and one of its attribute: textilizable(issue, :description, options)
470 def textilizable(*args)
470 def textilizable(*args)
471 options = args.last.is_a?(Hash) ? args.pop : {}
471 options = args.last.is_a?(Hash) ? args.pop : {}
472 case args.size
472 case args.size
473 when 1
473 when 1
474 obj = options[:object]
474 obj = options[:object]
475 text = args.shift
475 text = args.shift
476 when 2
476 when 2
477 obj = args.shift
477 obj = args.shift
478 attr = args.shift
478 attr = args.shift
479 text = obj.send(attr).to_s
479 text = obj.send(attr).to_s
480 else
480 else
481 raise ArgumentError, 'invalid arguments to textilizable'
481 raise ArgumentError, 'invalid arguments to textilizable'
482 end
482 end
483 return '' if text.blank?
483 return '' if text.blank?
484 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
484 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
485 only_path = options.delete(:only_path) == false ? false : true
485 only_path = options.delete(:only_path) == false ? false : true
486
486
487 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
487 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
488
488
489 @parsed_headings = []
489 @parsed_headings = []
490 text = parse_non_pre_blocks(text) do |text|
490 text = parse_non_pre_blocks(text) do |text|
491 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
491 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
492 send method_name, text, project, obj, attr, only_path, options
492 send method_name, text, project, obj, attr, only_path, options
493 end
493 end
494 end
494 end
495
495
496 if @parsed_headings.any?
496 if @parsed_headings.any?
497 replace_toc(text, @parsed_headings)
497 replace_toc(text, @parsed_headings)
498 end
498 end
499
499
500 text
500 text
501 end
501 end
502
502
503 def parse_non_pre_blocks(text)
503 def parse_non_pre_blocks(text)
504 s = StringScanner.new(text)
504 s = StringScanner.new(text)
505 tags = []
505 tags = []
506 parsed = ''
506 parsed = ''
507 while !s.eos?
507 while !s.eos?
508 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
508 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
509 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
509 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
510 if tags.empty?
510 if tags.empty?
511 yield text
511 yield text
512 end
512 end
513 parsed << text
513 parsed << text
514 if tag
514 if tag
515 if closing
515 if closing
516 if tags.last == tag.downcase
516 if tags.last == tag.downcase
517 tags.pop
517 tags.pop
518 end
518 end
519 else
519 else
520 tags << tag.downcase
520 tags << tag.downcase
521 end
521 end
522 parsed << full_tag
522 parsed << full_tag
523 end
523 end
524 end
524 end
525 # Close any non closing tags
525 # Close any non closing tags
526 while tag = tags.pop
526 while tag = tags.pop
527 parsed << "</#{tag}>"
527 parsed << "</#{tag}>"
528 end
528 end
529 parsed.html_safe
529 parsed.html_safe
530 end
530 end
531
531
532 def parse_inline_attachments(text, project, obj, attr, only_path, options)
532 def parse_inline_attachments(text, project, obj, attr, only_path, options)
533 # when using an image link, try to use an attachment, if possible
533 # when using an image link, try to use an attachment, if possible
534 if options[:attachments] || (obj && obj.respond_to?(:attachments))
534 if options[:attachments] || (obj && obj.respond_to?(:attachments))
535 attachments = nil
535 attachments = nil
536 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
536 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
537 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
537 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
538 attachments ||= (options[:attachments] || obj.attachments).sort_by(&:created_on).reverse
538 attachments ||= (options[:attachments] || obj.attachments).sort_by(&:created_on).reverse
539 # search for the picture in attachments
539 # search for the picture in attachments
540 if found = attachments.detect { |att| att.filename.downcase == filename }
540 if found = attachments.detect { |att| att.filename.downcase == filename }
541 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
541 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
542 desc = found.description.to_s.gsub('"', '')
542 desc = found.description.to_s.gsub('"', '')
543 if !desc.blank? && alttext.blank?
543 if !desc.blank? && alttext.blank?
544 alt = " title=\"#{desc}\" alt=\"#{desc}\""
544 alt = " title=\"#{desc}\" alt=\"#{desc}\""
545 end
545 end
546 "src=\"#{image_url}\"#{alt}".html_safe
546 "src=\"#{image_url}\"#{alt}".html_safe
547 else
547 else
548 m.html_safe
548 m.html_safe
549 end
549 end
550 end
550 end
551 end
551 end
552 end
552 end
553
553
554 # Wiki links
554 # Wiki links
555 #
555 #
556 # Examples:
556 # Examples:
557 # [[mypage]]
557 # [[mypage]]
558 # [[mypage|mytext]]
558 # [[mypage|mytext]]
559 # wiki links can refer other project wikis, using project name or identifier:
559 # wiki links can refer other project wikis, using project name or identifier:
560 # [[project:]] -> wiki starting page
560 # [[project:]] -> wiki starting page
561 # [[project:|mytext]]
561 # [[project:|mytext]]
562 # [[project:mypage]]
562 # [[project:mypage]]
563 # [[project:mypage|mytext]]
563 # [[project:mypage|mytext]]
564 def parse_wiki_links(text, project, obj, attr, only_path, options)
564 def parse_wiki_links(text, project, obj, attr, only_path, options)
565 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
565 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
566 link_project = project
566 link_project = project
567 esc, all, page, title = $1, $2, $3, $5
567 esc, all, page, title = $1, $2, $3, $5
568 if esc.nil?
568 if esc.nil?
569 if page =~ /^([^\:]+)\:(.*)$/
569 if page =~ /^([^\:]+)\:(.*)$/
570 link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
570 link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
571 page = $2
571 page = $2
572 title ||= $1 if page.blank?
572 title ||= $1 if page.blank?
573 end
573 end
574
574
575 if link_project && link_project.wiki
575 if link_project && link_project.wiki
576 # extract anchor
576 # extract anchor
577 anchor = nil
577 anchor = nil
578 if page =~ /^(.+?)\#(.+)$/
578 if page =~ /^(.+?)\#(.+)$/
579 page, anchor = $1, $2
579 page, anchor = $1, $2
580 end
580 end
581 anchor = sanitize_anchor_name(anchor) if anchor.present?
581 anchor = sanitize_anchor_name(anchor) if anchor.present?
582 # check if page exists
582 # check if page exists
583 wiki_page = link_project.wiki.find_page(page)
583 wiki_page = link_project.wiki.find_page(page)
584 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
584 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
585 "##{anchor}"
585 "##{anchor}"
586 else
586 else
587 case options[:wiki_links]
587 case options[:wiki_links]
588 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
588 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
589 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
589 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
590 else
590 else
591 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
591 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
592 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
592 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
593 end
593 end
594 end
594 end
595 link_to(h(title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
595 link_to(h(title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
596 else
596 else
597 # project or wiki doesn't exist
597 # project or wiki doesn't exist
598 all.html_safe
598 all.html_safe
599 end
599 end
600 else
600 else
601 all.html_safe
601 all.html_safe
602 end
602 end
603 end
603 end
604 end
604 end
605
605
606 # Redmine links
606 # Redmine links
607 #
607 #
608 # Examples:
608 # Examples:
609 # Issues:
609 # Issues:
610 # #52 -> Link to issue #52
610 # #52 -> Link to issue #52
611 # Changesets:
611 # Changesets:
612 # r52 -> Link to revision 52
612 # r52 -> Link to revision 52
613 # commit:a85130f -> Link to scmid starting with a85130f
613 # commit:a85130f -> Link to scmid starting with a85130f
614 # Documents:
614 # Documents:
615 # document#17 -> Link to document with id 17
615 # document#17 -> Link to document with id 17
616 # document:Greetings -> Link to the document with title "Greetings"
616 # document:Greetings -> Link to the document with title "Greetings"
617 # document:"Some document" -> Link to the document with title "Some document"
617 # document:"Some document" -> Link to the document with title "Some document"
618 # Versions:
618 # Versions:
619 # version#3 -> Link to version with id 3
619 # version#3 -> Link to version with id 3
620 # version:1.0.0 -> Link to version named "1.0.0"
620 # version:1.0.0 -> Link to version named "1.0.0"
621 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
621 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
622 # Attachments:
622 # Attachments:
623 # attachment:file.zip -> Link to the attachment of the current object named file.zip
623 # attachment:file.zip -> Link to the attachment of the current object named file.zip
624 # Source files:
624 # Source files:
625 # source:some/file -> Link to the file located at /some/file in the project's repository
625 # source:some/file -> Link to the file located at /some/file in the project's repository
626 # source:some/file@52 -> Link to the file's revision 52
626 # source:some/file@52 -> Link to the file's revision 52
627 # source:some/file#L120 -> Link to line 120 of the file
627 # source:some/file#L120 -> Link to line 120 of the file
628 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
628 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
629 # export:some/file -> Force the download of the file
629 # export:some/file -> Force the download of the file
630 # Forum messages:
630 # Forum messages:
631 # message#1218 -> Link to message with id 1218
631 # message#1218 -> Link to message with id 1218
632 #
632 #
633 # Links can refer other objects from other projects, using project identifier:
633 # Links can refer other objects from other projects, using project identifier:
634 # identifier:r52
634 # identifier:r52
635 # identifier:document:"Some document"
635 # identifier:document:"Some document"
636 # identifier:version:1.0.0
636 # identifier:version:1.0.0
637 # identifier:source:some/file
637 # identifier:source:some/file
638 def parse_redmine_links(text, project, obj, attr, only_path, options)
638 def parse_redmine_links(text, project, obj, attr, only_path, options)
639 text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
639 text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
640 leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
640 leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
641 link = nil
641 link = nil
642 if project_identifier
642 if project_identifier
643 project = Project.visible.find_by_identifier(project_identifier)
643 project = Project.visible.find_by_identifier(project_identifier)
644 end
644 end
645 if esc.nil?
645 if esc.nil?
646 if prefix.nil? && sep == 'r'
646 if prefix.nil? && sep == 'r'
647 # project.changesets.visible raises an SQL error because of a double join on repositories
647 # project.changesets.visible raises an SQL error because of a double join on repositories
648 if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
648 if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
649 link = link_to(h("#{project_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
649 link = link_to(h("#{project_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
650 :class => 'changeset',
650 :class => 'changeset',
651 :title => truncate_single_line(changeset.comments, :length => 100))
651 :title => truncate_single_line(changeset.comments, :length => 100))
652 end
652 end
653 elsif sep == '#'
653 elsif sep == '#'
654 oid = identifier.to_i
654 oid = identifier.to_i
655 case prefix
655 case prefix
656 when nil
656 when nil
657 if issue = Issue.visible.find_by_id(oid, :include => :status)
657 if issue = Issue.visible.find_by_id(oid, :include => :status)
658 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
658 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
659 :class => issue.css_classes,
659 :class => issue.css_classes,
660 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
660 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
661 end
661 end
662 when 'document'
662 when 'document'
663 if document = Document.visible.find_by_id(oid)
663 if document = Document.visible.find_by_id(oid)
664 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
664 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
665 :class => 'document'
665 :class => 'document'
666 end
666 end
667 when 'version'
667 when 'version'
668 if version = Version.visible.find_by_id(oid)
668 if version = Version.visible.find_by_id(oid)
669 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
669 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
670 :class => 'version'
670 :class => 'version'
671 end
671 end
672 when 'message'
672 when 'message'
673 if message = Message.visible.find_by_id(oid, :include => :parent)
673 if message = Message.visible.find_by_id(oid, :include => :parent)
674 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
674 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
675 end
675 end
676 when 'project'
676 when 'project'
677 if p = Project.visible.find_by_id(oid)
677 if p = Project.visible.find_by_id(oid)
678 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
678 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
679 end
679 end
680 end
680 end
681 elsif sep == ':'
681 elsif sep == ':'
682 # removes the double quotes if any
682 # removes the double quotes if any
683 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
683 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
684 case prefix
684 case prefix
685 when 'document'
685 when 'document'
686 if project && document = project.documents.visible.find_by_title(name)
686 if project && document = project.documents.visible.find_by_title(name)
687 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
687 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
688 :class => 'document'
688 :class => 'document'
689 end
689 end
690 when 'version'
690 when 'version'
691 if project && version = project.versions.visible.find_by_name(name)
691 if project && version = project.versions.visible.find_by_name(name)
692 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
692 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
693 :class => 'version'
693 :class => 'version'
694 end
694 end
695 when 'commit'
695 when 'commit'
696 if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
696 if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
697 link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
697 link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
698 :class => 'changeset',
698 :class => 'changeset',
699 :title => truncate_single_line(h(changeset.comments), :length => 100)
699 :title => truncate_single_line(h(changeset.comments), :length => 100)
700 end
700 end
701 when 'source', 'export'
701 when 'source', 'export'
702 if project && project.repository && User.current.allowed_to?(:browse_repository, project)
702 if project && project.repository && User.current.allowed_to?(:browse_repository, project)
703 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
703 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
704 path, rev, anchor = $1, $3, $5
704 path, rev, anchor = $1, $3, $5
705 link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
705 link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
706 :path => to_path_param(path),
706 :path => to_path_param(path),
707 :rev => rev,
707 :rev => rev,
708 :anchor => anchor,
708 :anchor => anchor,
709 :format => (prefix == 'export' ? 'raw' : nil)},
709 :format => (prefix == 'export' ? 'raw' : nil)},
710 :class => (prefix == 'export' ? 'source download' : 'source')
710 :class => (prefix == 'export' ? 'source download' : 'source')
711 end
711 end
712 when 'attachment'
712 when 'attachment'
713 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
713 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
714 if attachments && attachment = attachments.detect {|a| a.filename == name }
714 if attachments && attachment = attachments.detect {|a| a.filename == name }
715 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
715 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
716 :class => 'attachment'
716 :class => 'attachment'
717 end
717 end
718 when 'project'
718 when 'project'
719 if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
719 if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
720 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
720 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
721 end
721 end
722 end
722 end
723 end
723 end
724 end
724 end
725 (leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")).html_safe
725 (leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")).html_safe
726 end
726 end
727 end
727 end
728
728
729 HEADING_RE = /<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>/i unless const_defined?(:HEADING_RE)
729 HEADING_RE = /<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>/i unless const_defined?(:HEADING_RE)
730
730
731 # Headings and TOC
731 # Headings and TOC
732 # Adds ids and links to headings unless options[:headings] is set to false
732 # Adds ids and links to headings unless options[:headings] is set to false
733 def parse_headings(text, project, obj, attr, only_path, options)
733 def parse_headings(text, project, obj, attr, only_path, options)
734 return if options[:headings] == false
734 return if options[:headings] == false
735
735
736 text.gsub!(HEADING_RE) do
736 text.gsub!(HEADING_RE) do
737 level, attrs, content = $1.to_i, $2, $3
737 level, attrs, content = $1.to_i, $2, $3
738 item = strip_tags(content).strip
738 item = strip_tags(content).strip
739 anchor = sanitize_anchor_name(item)
739 anchor = sanitize_anchor_name(item)
740 # used for single-file wiki export
740 # used for single-file wiki export
741 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
741 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
742 @parsed_headings << [level, anchor, item]
742 @parsed_headings << [level, anchor, item]
743 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
743 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
744 end
744 end
745 end
745 end
746
746
747 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
747 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
748
748
749 # Renders the TOC with given headings
749 # Renders the TOC with given headings
750 def replace_toc(text, headings)
750 def replace_toc(text, headings)
751 text.gsub!(TOC_RE) do
751 text.gsub!(TOC_RE) do
752 if headings.empty?
752 if headings.empty?
753 ''
753 ''
754 else
754 else
755 div_class = 'toc'
755 div_class = 'toc'
756 div_class << ' right' if $1 == '>'
756 div_class << ' right' if $1 == '>'
757 div_class << ' left' if $1 == '<'
757 div_class << ' left' if $1 == '<'
758 out = "<ul class=\"#{div_class}\"><li>"
758 out = "<ul class=\"#{div_class}\"><li>"
759 root = headings.map(&:first).min
759 root = headings.map(&:first).min
760 current = root
760 current = root
761 started = false
761 started = false
762 headings.each do |level, anchor, item|
762 headings.each do |level, anchor, item|
763 if level > current
763 if level > current
764 out << '<ul><li>' * (level - current)
764 out << '<ul><li>' * (level - current)
765 elsif level < current
765 elsif level < current
766 out << "</li></ul>\n" * (current - level) + "</li><li>"
766 out << "</li></ul>\n" * (current - level) + "</li><li>"
767 elsif started
767 elsif started
768 out << '</li><li>'
768 out << '</li><li>'
769 end
769 end
770 out << "<a href=\"##{anchor}\">#{item}</a>"
770 out << "<a href=\"##{anchor}\">#{item}</a>"
771 current = level
771 current = level
772 started = true
772 started = true
773 end
773 end
774 out << '</li></ul>' * (current - root)
774 out << '</li></ul>' * (current - root)
775 out << '</li></ul>'
775 out << '</li></ul>'
776 end
776 end
777 end
777 end
778 end
778 end
779
779
780 # Same as Rails' simple_format helper without using paragraphs
780 # Same as Rails' simple_format helper without using paragraphs
781 def simple_format_without_paragraph(text)
781 def simple_format_without_paragraph(text)
782 text.to_s.
782 text.to_s.
783 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
783 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
784 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
784 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
785 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
785 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />'). # 1 newline -> br
786 html_safe
786 html_safe
787 end
787 end
788
788
789 def lang_options_for_select(blank=true)
789 def lang_options_for_select(blank=true)
790 (blank ? [["(auto)", ""]] : []) +
790 (blank ? [["(auto)", ""]] : []) +
791 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
791 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
792 end
792 end
793
793
794 def label_tag_for(name, option_tags = nil, options = {})
794 def label_tag_for(name, option_tags = nil, options = {})
795 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
795 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
796 content_tag("label", label_text)
796 content_tag("label", label_text)
797 end
797 end
798
798
799 def labelled_tabular_form_for(name, object, options, &proc)
799 def labelled_tabular_form_for(name, object, options, &proc)
800 options[:html] ||= {}
800 options[:html] ||= {}
801 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
801 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
802 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
802 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
803 end
803 end
804
804
805 def back_url_hidden_field_tag
805 def back_url_hidden_field_tag
806 back_url = params[:back_url] || request.env['HTTP_REFERER']
806 back_url = params[:back_url] || request.env['HTTP_REFERER']
807 back_url = CGI.unescape(back_url.to_s)
807 back_url = CGI.unescape(back_url.to_s)
808 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
808 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
809 end
809 end
810
810
811 def check_all_links(form_name)
811 def check_all_links(form_name)
812 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
812 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
813 " | ".html_safe +
813 " | ".html_safe +
814 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
814 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
815 end
815 end
816
816
817 def progress_bar(pcts, options={})
817 def progress_bar(pcts, options={})
818 pcts = [pcts, pcts] unless pcts.is_a?(Array)
818 pcts = [pcts, pcts] unless pcts.is_a?(Array)
819 pcts = pcts.collect(&:round)
819 pcts = pcts.collect(&:round)
820 pcts[1] = pcts[1] - pcts[0]
820 pcts[1] = pcts[1] - pcts[0]
821 pcts << (100 - pcts[1] - pcts[0])
821 pcts << (100 - pcts[1] - pcts[0])
822 width = options[:width] || '100px;'
822 width = options[:width] || '100px;'
823 legend = options[:legend] || ''
823 legend = options[:legend] || ''
824 content_tag('table',
824 content_tag('table',
825 content_tag('tr',
825 content_tag('tr',
826 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
826 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
827 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
827 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
828 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
828 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
829 ), :class => 'progress', :style => "width: #{width};").html_safe +
829 ), :class => 'progress', :style => "width: #{width};").html_safe +
830 content_tag('p', legend, :class => 'pourcent').html_safe
830 content_tag('p', legend, :class => 'pourcent').html_safe
831 end
831 end
832
832
833 def checked_image(checked=true)
833 def checked_image(checked=true)
834 if checked
834 if checked
835 image_tag 'toggle_check.png'
835 image_tag 'toggle_check.png'
836 end
836 end
837 end
837 end
838
838
839 def context_menu(url)
839 def context_menu(url)
840 unless @context_menu_included
840 unless @context_menu_included
841 content_for :header_tags do
841 content_for :header_tags do
842 javascript_include_tag('context_menu') +
842 javascript_include_tag('context_menu') +
843 stylesheet_link_tag('context_menu')
843 stylesheet_link_tag('context_menu')
844 end
844 end
845 if l(:direction) == 'rtl'
845 if l(:direction) == 'rtl'
846 content_for :header_tags do
846 content_for :header_tags do
847 stylesheet_link_tag('context_menu_rtl')
847 stylesheet_link_tag('context_menu_rtl')
848 end
848 end
849 end
849 end
850 @context_menu_included = true
850 @context_menu_included = true
851 end
851 end
852 javascript_tag "new ContextMenu('#{ url_for(url) }')"
852 javascript_tag "new ContextMenu('#{ url_for(url) }')"
853 end
853 end
854
854
855 def context_menu_link(name, url, options={})
855 def context_menu_link(name, url, options={})
856 options[:class] ||= ''
856 options[:class] ||= ''
857 if options.delete(:selected)
857 if options.delete(:selected)
858 options[:class] << ' icon-checked disabled'
858 options[:class] << ' icon-checked disabled'
859 options[:disabled] = true
859 options[:disabled] = true
860 end
860 end
861 if options.delete(:disabled)
861 if options.delete(:disabled)
862 options.delete(:method)
862 options.delete(:method)
863 options.delete(:confirm)
863 options.delete(:confirm)
864 options.delete(:onclick)
864 options.delete(:onclick)
865 options[:class] << ' disabled'
865 options[:class] << ' disabled'
866 url = '#'
866 url = '#'
867 end
867 end
868 link_to h(name), url, options
868 link_to h(name), url, options
869 end
869 end
870
870
871 def calendar_for(field_id)
871 def calendar_for(field_id)
872 include_calendar_headers_tags
872 include_calendar_headers_tags
873 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
873 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
874 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
874 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
875 end
875 end
876
876
877 def include_calendar_headers_tags
877 def include_calendar_headers_tags
878 unless @calendar_headers_tags_included
878 unless @calendar_headers_tags_included
879 @calendar_headers_tags_included = true
879 @calendar_headers_tags_included = true
880 content_for :header_tags do
880 content_for :header_tags do
881 start_of_week = case Setting.start_of_week.to_i
881 start_of_week = case Setting.start_of_week.to_i
882 when 1
882 when 1
883 'Calendar._FD = 1;' # Monday
883 'Calendar._FD = 1;' # Monday
884 when 7
884 when 7
885 'Calendar._FD = 0;' # Sunday
885 'Calendar._FD = 0;' # Sunday
886 when 6
886 when 6
887 'Calendar._FD = 6;' # Saturday
887 'Calendar._FD = 6;' # Saturday
888 else
888 else
889 '' # use language
889 '' # use language
890 end
890 end
891
891
892 javascript_include_tag('calendar/calendar') +
892 javascript_include_tag('calendar/calendar') +
893 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
893 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
894 javascript_tag(start_of_week) +
894 javascript_tag(start_of_week) +
895 javascript_include_tag('calendar/calendar-setup') +
895 javascript_include_tag('calendar/calendar-setup') +
896 stylesheet_link_tag('calendar')
896 stylesheet_link_tag('calendar')
897 end
897 end
898 end
898 end
899 end
899 end
900
900
901 def content_for(name, content = nil, &block)
901 def content_for(name, content = nil, &block)
902 @has_content ||= {}
902 @has_content ||= {}
903 @has_content[name] = true
903 @has_content[name] = true
904 super(name, content, &block)
904 super(name, content, &block)
905 end
905 end
906
906
907 def has_content?(name)
907 def has_content?(name)
908 (@has_content && @has_content[name]) || false
908 (@has_content && @has_content[name]) || false
909 end
909 end
910
910
911 def email_delivery_enabled?
911 def email_delivery_enabled?
912 !!ActionMailer::Base.perform_deliveries
912 !!ActionMailer::Base.perform_deliveries
913 end
913 end
914
914
915 # Returns the avatar image tag for the given +user+ if avatars are enabled
915 # Returns the avatar image tag for the given +user+ if avatars are enabled
916 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
916 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
917 def avatar(user, options = { })
917 def avatar(user, options = { })
918 if Setting.gravatar_enabled?
918 if Setting.gravatar_enabled?
919 options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
919 options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
920 email = nil
920 email = nil
921 if user.respond_to?(:mail)
921 if user.respond_to?(:mail)
922 email = user.mail
922 email = user.mail
923 elsif user.to_s =~ %r{<(.+?)>}
923 elsif user.to_s =~ %r{<(.+?)>}
924 email = $1
924 email = $1
925 end
925 end
926 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
926 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
927 else
927 else
928 ''
928 ''
929 end
929 end
930 end
930 end
931
931
932 def sanitize_anchor_name(anchor)
932 def sanitize_anchor_name(anchor)
933 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
933 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
934 end
934 end
935
935
936 # Returns the javascript tags that are included in the html layout head
936 # Returns the javascript tags that are included in the html layout head
937 def javascript_heads
937 def javascript_heads
938 tags = javascript_include_tag(:defaults)
938 tags = javascript_include_tag(:defaults)
939 unless User.current.pref.warn_on_leaving_unsaved == '0'
939 unless User.current.pref.warn_on_leaving_unsaved == '0'
940 tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
940 tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
941 end
941 end
942 tags
942 tags
943 end
943 end
944
944
945 def favicon
945 def favicon
946 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
946 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
947 end
947 end
948
948
949 def robot_exclusion_tag
949 def robot_exclusion_tag
950 '<meta name="robots" content="noindex,follow,noarchive" />'
950 '<meta name="robots" content="noindex,follow,noarchive" />'
951 end
951 end
952
952
953 # Returns true if arg is expected in the API response
953 # Returns true if arg is expected in the API response
954 def include_in_api_response?(arg)
954 def include_in_api_response?(arg)
955 unless @included_in_api_response
955 unless @included_in_api_response
956 param = params[:include]
956 param = params[:include]
957 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
957 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
958 @included_in_api_response.collect!(&:strip)
958 @included_in_api_response.collect!(&:strip)
959 end
959 end
960 @included_in_api_response.include?(arg.to_s)
960 @included_in_api_response.include?(arg.to_s)
961 end
961 end
962
962
963 # Returns options or nil if nometa param or X-Redmine-Nometa header
963 # Returns options or nil if nometa param or X-Redmine-Nometa header
964 # was set in the request
964 # was set in the request
965 def api_meta(options)
965 def api_meta(options)
966 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
966 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
967 # compatibility mode for activeresource clients that raise
967 # compatibility mode for activeresource clients that raise
968 # an error when unserializing an array with attributes
968 # an error when unserializing an array with attributes
969 nil
969 nil
970 else
970 else
971 options
971 options
972 end
972 end
973 end
973 end
974
974
975 private
975 private
976
976
977 def wiki_helper
977 def wiki_helper
978 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
978 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
979 extend helper
979 extend helper
980 return self
980 return self
981 end
981 end
982
982
983 def link_to_content_update(text, url_params = {}, html_options = {})
983 def link_to_content_update(text, url_params = {}, html_options = {})
984 link_to(text, url_params, html_options)
984 link_to(text, url_params, html_options)
985 end
985 end
986 end
986 end
General Comments 0
You need to be logged in to leave comments. Login now