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