##// END OF EJS Templates
Rails3: helper: use html_safe at render_page_hierarchy of ApplicationHelper...
Toshi MARUYAMA -
r7464:942cd9bac2be
parent child
Show More
@@ -1,977 +1,977
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
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)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
391 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
392 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
392 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
393 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
393 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
394 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
394 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
395 end
395 end
396
396
397 def breadcrumb(*args)
397 def breadcrumb(*args)
398 elements = args.flatten
398 elements = args.flatten
399 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
399 elements.any? ? content_tag('p', (args.join(" \xc2\xbb ") + " \xc2\xbb ").html_safe, :class => 'breadcrumb') : nil
400 end
400 end
401
401
402 def other_formats_links(&block)
402 def other_formats_links(&block)
403 concat('<p class="other-formats">' + l(:label_export_to))
403 concat('<p class="other-formats">' + l(:label_export_to))
404 yield Redmine::Views::OtherFormatsBuilder.new(self)
404 yield Redmine::Views::OtherFormatsBuilder.new(self)
405 concat('</p>')
405 concat('</p>')
406 end
406 end
407
407
408 def page_header_title
408 def page_header_title
409 if @project.nil? || @project.new_record?
409 if @project.nil? || @project.new_record?
410 h(Setting.app_title)
410 h(Setting.app_title)
411 else
411 else
412 b = []
412 b = []
413 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
413 ancestors = (@project.root? ? [] : @project.ancestors.visible.all)
414 if ancestors.any?
414 if ancestors.any?
415 root = ancestors.shift
415 root = ancestors.shift
416 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
416 b << link_to_project(root, {:jump => current_menu_item}, :class => 'root')
417 if ancestors.size > 2
417 if ancestors.size > 2
418 b << "\xe2\x80\xa6"
418 b << "\xe2\x80\xa6"
419 ancestors = ancestors[-2, 2]
419 ancestors = ancestors[-2, 2]
420 end
420 end
421 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
421 b += ancestors.collect {|p| link_to_project(p, {:jump => current_menu_item}, :class => 'ancestor') }
422 end
422 end
423 b << h(@project)
423 b << h(@project)
424 b.join(" \xc2\xbb ")
424 b.join(" \xc2\xbb ")
425 end
425 end
426 end
426 end
427
427
428 def html_title(*args)
428 def html_title(*args)
429 if args.empty?
429 if args.empty?
430 title = []
430 title = []
431 title << @project.name if @project
431 title << @project.name if @project
432 title += @html_title if @html_title
432 title += @html_title if @html_title
433 title << Setting.app_title
433 title << Setting.app_title
434 title.select {|t| !t.blank? }.join(' - ')
434 title.select {|t| !t.blank? }.join(' - ')
435 else
435 else
436 @html_title ||= []
436 @html_title ||= []
437 @html_title += args
437 @html_title += args
438 end
438 end
439 end
439 end
440
440
441 # Returns the theme, controller name, and action as css classes for the
441 # Returns the theme, controller name, and action as css classes for the
442 # HTML body.
442 # HTML body.
443 def body_css_classes
443 def body_css_classes
444 css = []
444 css = []
445 if theme = Redmine::Themes.theme(Setting.ui_theme)
445 if theme = Redmine::Themes.theme(Setting.ui_theme)
446 css << 'theme-' + theme.name
446 css << 'theme-' + theme.name
447 end
447 end
448
448
449 css << 'controller-' + params[:controller]
449 css << 'controller-' + params[:controller]
450 css << 'action-' + params[:action]
450 css << 'action-' + params[:action]
451 css.join(' ')
451 css.join(' ')
452 end
452 end
453
453
454 def accesskey(s)
454 def accesskey(s)
455 Redmine::AccessKeys.key_for s
455 Redmine::AccessKeys.key_for s
456 end
456 end
457
457
458 # Formats text according to system settings.
458 # Formats text according to system settings.
459 # 2 ways to call this method:
459 # 2 ways to call this method:
460 # * with a String: textilizable(text, options)
460 # * with a String: textilizable(text, options)
461 # * with an object and one of its attribute: textilizable(issue, :description, options)
461 # * with an object and one of its attribute: textilizable(issue, :description, options)
462 def textilizable(*args)
462 def textilizable(*args)
463 options = args.last.is_a?(Hash) ? args.pop : {}
463 options = args.last.is_a?(Hash) ? args.pop : {}
464 case args.size
464 case args.size
465 when 1
465 when 1
466 obj = options[:object]
466 obj = options[:object]
467 text = args.shift
467 text = args.shift
468 when 2
468 when 2
469 obj = args.shift
469 obj = args.shift
470 attr = args.shift
470 attr = args.shift
471 text = obj.send(attr).to_s
471 text = obj.send(attr).to_s
472 else
472 else
473 raise ArgumentError, 'invalid arguments to textilizable'
473 raise ArgumentError, 'invalid arguments to textilizable'
474 end
474 end
475 return '' if text.blank?
475 return '' if text.blank?
476 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
476 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
477 only_path = options.delete(:only_path) == false ? false : true
477 only_path = options.delete(:only_path) == false ? false : true
478
478
479 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
479 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
480
480
481 @parsed_headings = []
481 @parsed_headings = []
482 text = parse_non_pre_blocks(text) do |text|
482 text = parse_non_pre_blocks(text) do |text|
483 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
483 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
484 send method_name, text, project, obj, attr, only_path, options
484 send method_name, text, project, obj, attr, only_path, options
485 end
485 end
486 end
486 end
487
487
488 if @parsed_headings.any?
488 if @parsed_headings.any?
489 replace_toc(text, @parsed_headings)
489 replace_toc(text, @parsed_headings)
490 end
490 end
491
491
492 text
492 text
493 end
493 end
494
494
495 def parse_non_pre_blocks(text)
495 def parse_non_pre_blocks(text)
496 s = StringScanner.new(text)
496 s = StringScanner.new(text)
497 tags = []
497 tags = []
498 parsed = ''
498 parsed = ''
499 while !s.eos?
499 while !s.eos?
500 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
500 s.scan(/(.*?)(<(\/)?(pre|code)(.*?)>|\z)/im)
501 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
501 text, full_tag, closing, tag = s[1], s[2], s[3], s[4]
502 if tags.empty?
502 if tags.empty?
503 yield text
503 yield text
504 end
504 end
505 parsed << text
505 parsed << text
506 if tag
506 if tag
507 if closing
507 if closing
508 if tags.last == tag.downcase
508 if tags.last == tag.downcase
509 tags.pop
509 tags.pop
510 end
510 end
511 else
511 else
512 tags << tag.downcase
512 tags << tag.downcase
513 end
513 end
514 parsed << full_tag
514 parsed << full_tag
515 end
515 end
516 end
516 end
517 # Close any non closing tags
517 # Close any non closing tags
518 while tag = tags.pop
518 while tag = tags.pop
519 parsed << "</#{tag}>"
519 parsed << "</#{tag}>"
520 end
520 end
521 parsed.html_safe
521 parsed.html_safe
522 end
522 end
523
523
524 def parse_inline_attachments(text, project, obj, attr, only_path, options)
524 def parse_inline_attachments(text, project, obj, attr, only_path, options)
525 # when using an image link, try to use an attachment, if possible
525 # when using an image link, try to use an attachment, if possible
526 if options[:attachments] || (obj && obj.respond_to?(:attachments))
526 if options[:attachments] || (obj && obj.respond_to?(:attachments))
527 attachments = nil
527 attachments = nil
528 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
528 text.gsub!(/src="([^\/"]+\.(bmp|gif|jpg|jpeg|png))"(\s+alt="([^"]*)")?/i) do |m|
529 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
529 filename, ext, alt, alttext = $1.downcase, $2, $3, $4
530 attachments ||= (options[:attachments] || obj.attachments).sort_by(&:created_on).reverse
530 attachments ||= (options[:attachments] || obj.attachments).sort_by(&:created_on).reverse
531 # search for the picture in attachments
531 # search for the picture in attachments
532 if found = attachments.detect { |att| att.filename.downcase == filename }
532 if found = attachments.detect { |att| att.filename.downcase == filename }
533 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
533 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
534 desc = found.description.to_s.gsub('"', '')
534 desc = found.description.to_s.gsub('"', '')
535 if !desc.blank? && alttext.blank?
535 if !desc.blank? && alttext.blank?
536 alt = " title=\"#{desc}\" alt=\"#{desc}\""
536 alt = " title=\"#{desc}\" alt=\"#{desc}\""
537 end
537 end
538 "src=\"#{image_url}\"#{alt}".html_safe
538 "src=\"#{image_url}\"#{alt}".html_safe
539 else
539 else
540 m.html_safe
540 m.html_safe
541 end
541 end
542 end
542 end
543 end
543 end
544 end
544 end
545
545
546 # Wiki links
546 # Wiki links
547 #
547 #
548 # Examples:
548 # Examples:
549 # [[mypage]]
549 # [[mypage]]
550 # [[mypage|mytext]]
550 # [[mypage|mytext]]
551 # wiki links can refer other project wikis, using project name or identifier:
551 # wiki links can refer other project wikis, using project name or identifier:
552 # [[project:]] -> wiki starting page
552 # [[project:]] -> wiki starting page
553 # [[project:|mytext]]
553 # [[project:|mytext]]
554 # [[project:mypage]]
554 # [[project:mypage]]
555 # [[project:mypage|mytext]]
555 # [[project:mypage|mytext]]
556 def parse_wiki_links(text, project, obj, attr, only_path, options)
556 def parse_wiki_links(text, project, obj, attr, only_path, options)
557 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
557 text.gsub!(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
558 link_project = project
558 link_project = project
559 esc, all, page, title = $1, $2, $3, $5
559 esc, all, page, title = $1, $2, $3, $5
560 if esc.nil?
560 if esc.nil?
561 if page =~ /^([^\:]+)\:(.*)$/
561 if page =~ /^([^\:]+)\:(.*)$/
562 link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
562 link_project = Project.find_by_identifier($1) || Project.find_by_name($1)
563 page = $2
563 page = $2
564 title ||= $1 if page.blank?
564 title ||= $1 if page.blank?
565 end
565 end
566
566
567 if link_project && link_project.wiki
567 if link_project && link_project.wiki
568 # extract anchor
568 # extract anchor
569 anchor = nil
569 anchor = nil
570 if page =~ /^(.+?)\#(.+)$/
570 if page =~ /^(.+?)\#(.+)$/
571 page, anchor = $1, $2
571 page, anchor = $1, $2
572 end
572 end
573 anchor = sanitize_anchor_name(anchor) if anchor.present?
573 anchor = sanitize_anchor_name(anchor) if anchor.present?
574 # check if page exists
574 # check if page exists
575 wiki_page = link_project.wiki.find_page(page)
575 wiki_page = link_project.wiki.find_page(page)
576 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
576 url = if anchor.present? && wiki_page.present? && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version)) && obj.page == wiki_page
577 "##{anchor}"
577 "##{anchor}"
578 else
578 else
579 case options[:wiki_links]
579 case options[:wiki_links]
580 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
580 when :local; "#{page.present? ? Wiki.titleize(page) : ''}.html" + (anchor.present? ? "##{anchor}" : '')
581 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
581 when :anchor; "##{page.present? ? Wiki.titleize(page) : title}" + (anchor.present? ? "_#{anchor}" : '') # used for single-file wiki export
582 else
582 else
583 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
583 wiki_page_id = page.present? ? Wiki.titleize(page) : nil
584 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
584 url_for(:only_path => only_path, :controller => 'wiki', :action => 'show', :project_id => link_project, :id => wiki_page_id, :anchor => anchor)
585 end
585 end
586 end
586 end
587 link_to(h(title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
587 link_to(h(title || page), url, :class => ('wiki-page' + (wiki_page ? '' : ' new')))
588 else
588 else
589 # project or wiki doesn't exist
589 # project or wiki doesn't exist
590 all.html_safe
590 all.html_safe
591 end
591 end
592 else
592 else
593 all.html_safe
593 all.html_safe
594 end
594 end
595 end
595 end
596 end
596 end
597
597
598 # Redmine links
598 # Redmine links
599 #
599 #
600 # Examples:
600 # Examples:
601 # Issues:
601 # Issues:
602 # #52 -> Link to issue #52
602 # #52 -> Link to issue #52
603 # Changesets:
603 # Changesets:
604 # r52 -> Link to revision 52
604 # r52 -> Link to revision 52
605 # commit:a85130f -> Link to scmid starting with a85130f
605 # commit:a85130f -> Link to scmid starting with a85130f
606 # Documents:
606 # Documents:
607 # document#17 -> Link to document with id 17
607 # document#17 -> Link to document with id 17
608 # document:Greetings -> Link to the document with title "Greetings"
608 # document:Greetings -> Link to the document with title "Greetings"
609 # document:"Some document" -> Link to the document with title "Some document"
609 # document:"Some document" -> Link to the document with title "Some document"
610 # Versions:
610 # Versions:
611 # version#3 -> Link to version with id 3
611 # version#3 -> Link to version with id 3
612 # version:1.0.0 -> Link to version named "1.0.0"
612 # version:1.0.0 -> Link to version named "1.0.0"
613 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
613 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
614 # Attachments:
614 # Attachments:
615 # attachment:file.zip -> Link to the attachment of the current object named file.zip
615 # attachment:file.zip -> Link to the attachment of the current object named file.zip
616 # Source files:
616 # Source files:
617 # source:some/file -> Link to the file located at /some/file in the project's repository
617 # source:some/file -> Link to the file located at /some/file in the project's repository
618 # source:some/file@52 -> Link to the file's revision 52
618 # source:some/file@52 -> Link to the file's revision 52
619 # source:some/file#L120 -> Link to line 120 of the file
619 # source:some/file#L120 -> Link to line 120 of the file
620 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
620 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
621 # export:some/file -> Force the download of the file
621 # export:some/file -> Force the download of the file
622 # Forum messages:
622 # Forum messages:
623 # message#1218 -> Link to message with id 1218
623 # message#1218 -> Link to message with id 1218
624 #
624 #
625 # Links can refer other objects from other projects, using project identifier:
625 # Links can refer other objects from other projects, using project identifier:
626 # identifier:r52
626 # identifier:r52
627 # identifier:document:"Some document"
627 # identifier:document:"Some document"
628 # identifier:version:1.0.0
628 # identifier:version:1.0.0
629 # identifier:source:some/file
629 # identifier:source:some/file
630 def parse_redmine_links(text, project, obj, attr, only_path, options)
630 def parse_redmine_links(text, project, obj, attr, only_path, options)
631 text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
631 text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
632 leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
632 leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
633 link = nil
633 link = nil
634 if project_identifier
634 if project_identifier
635 project = Project.visible.find_by_identifier(project_identifier)
635 project = Project.visible.find_by_identifier(project_identifier)
636 end
636 end
637 if esc.nil?
637 if esc.nil?
638 if prefix.nil? && sep == 'r'
638 if prefix.nil? && sep == 'r'
639 # project.changesets.visible raises an SQL error because of a double join on repositories
639 # project.changesets.visible raises an SQL error because of a double join on repositories
640 if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
640 if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
641 link = link_to(h("#{project_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
641 link = link_to(h("#{project_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
642 :class => 'changeset',
642 :class => 'changeset',
643 :title => truncate_single_line(changeset.comments, :length => 100))
643 :title => truncate_single_line(changeset.comments, :length => 100))
644 end
644 end
645 elsif sep == '#'
645 elsif sep == '#'
646 oid = identifier.to_i
646 oid = identifier.to_i
647 case prefix
647 case prefix
648 when nil
648 when nil
649 if issue = Issue.visible.find_by_id(oid, :include => :status)
649 if issue = Issue.visible.find_by_id(oid, :include => :status)
650 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
650 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
651 :class => issue.css_classes,
651 :class => issue.css_classes,
652 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
652 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
653 end
653 end
654 when 'document'
654 when 'document'
655 if document = Document.visible.find_by_id(oid)
655 if document = Document.visible.find_by_id(oid)
656 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
656 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
657 :class => 'document'
657 :class => 'document'
658 end
658 end
659 when 'version'
659 when 'version'
660 if version = Version.visible.find_by_id(oid)
660 if version = Version.visible.find_by_id(oid)
661 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
661 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
662 :class => 'version'
662 :class => 'version'
663 end
663 end
664 when 'message'
664 when 'message'
665 if message = Message.visible.find_by_id(oid, :include => :parent)
665 if message = Message.visible.find_by_id(oid, :include => :parent)
666 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
666 link = link_to_message(message, {:only_path => only_path}, :class => 'message')
667 end
667 end
668 when 'project'
668 when 'project'
669 if p = Project.visible.find_by_id(oid)
669 if p = Project.visible.find_by_id(oid)
670 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
670 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
671 end
671 end
672 end
672 end
673 elsif sep == ':'
673 elsif sep == ':'
674 # removes the double quotes if any
674 # removes the double quotes if any
675 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
675 name = identifier.gsub(%r{^"(.*)"$}, "\\1")
676 case prefix
676 case prefix
677 when 'document'
677 when 'document'
678 if project && document = project.documents.visible.find_by_title(name)
678 if project && document = project.documents.visible.find_by_title(name)
679 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
679 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
680 :class => 'document'
680 :class => 'document'
681 end
681 end
682 when 'version'
682 when 'version'
683 if project && version = project.versions.visible.find_by_name(name)
683 if project && version = project.versions.visible.find_by_name(name)
684 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
684 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
685 :class => 'version'
685 :class => 'version'
686 end
686 end
687 when 'commit'
687 when 'commit'
688 if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
688 if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
689 link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
689 link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
690 :class => 'changeset',
690 :class => 'changeset',
691 :title => truncate_single_line(h(changeset.comments), :length => 100)
691 :title => truncate_single_line(h(changeset.comments), :length => 100)
692 end
692 end
693 when 'source', 'export'
693 when 'source', 'export'
694 if project && project.repository && User.current.allowed_to?(:browse_repository, project)
694 if project && project.repository && User.current.allowed_to?(:browse_repository, project)
695 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
695 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
696 path, rev, anchor = $1, $3, $5
696 path, rev, anchor = $1, $3, $5
697 link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
697 link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
698 :path => to_path_param(path),
698 :path => to_path_param(path),
699 :rev => rev,
699 :rev => rev,
700 :anchor => anchor,
700 :anchor => anchor,
701 :format => (prefix == 'export' ? 'raw' : nil)},
701 :format => (prefix == 'export' ? 'raw' : nil)},
702 :class => (prefix == 'export' ? 'source download' : 'source')
702 :class => (prefix == 'export' ? 'source download' : 'source')
703 end
703 end
704 when 'attachment'
704 when 'attachment'
705 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
705 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
706 if attachments && attachment = attachments.detect {|a| a.filename == name }
706 if attachments && attachment = attachments.detect {|a| a.filename == name }
707 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
707 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
708 :class => 'attachment'
708 :class => 'attachment'
709 end
709 end
710 when 'project'
710 when 'project'
711 if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
711 if p = Project.visible.find(:first, :conditions => ["identifier = :s OR LOWER(name) = :s", {:s => name.downcase}])
712 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
712 link = link_to_project(p, {:only_path => only_path}, :class => 'project')
713 end
713 end
714 end
714 end
715 end
715 end
716 end
716 end
717 (leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")).html_safe
717 (leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")).html_safe
718 end
718 end
719 end
719 end
720
720
721 HEADING_RE = /<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>/i unless const_defined?(:HEADING_RE)
721 HEADING_RE = /<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>/i unless const_defined?(:HEADING_RE)
722
722
723 # Headings and TOC
723 # Headings and TOC
724 # Adds ids and links to headings unless options[:headings] is set to false
724 # Adds ids and links to headings unless options[:headings] is set to false
725 def parse_headings(text, project, obj, attr, only_path, options)
725 def parse_headings(text, project, obj, attr, only_path, options)
726 return if options[:headings] == false
726 return if options[:headings] == false
727
727
728 text.gsub!(HEADING_RE) do
728 text.gsub!(HEADING_RE) do
729 level, attrs, content = $1.to_i, $2, $3
729 level, attrs, content = $1.to_i, $2, $3
730 item = strip_tags(content).strip
730 item = strip_tags(content).strip
731 anchor = sanitize_anchor_name(item)
731 anchor = sanitize_anchor_name(item)
732 # used for single-file wiki export
732 # used for single-file wiki export
733 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
733 anchor = "#{obj.page.title}_#{anchor}" if options[:wiki_links] == :anchor && (obj.is_a?(WikiContent) || obj.is_a?(WikiContent::Version))
734 @parsed_headings << [level, anchor, item]
734 @parsed_headings << [level, anchor, item]
735 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
735 "<a name=\"#{anchor}\"></a>\n<h#{level} #{attrs}>#{content}<a href=\"##{anchor}\" class=\"wiki-anchor\">&para;</a></h#{level}>"
736 end
736 end
737 end
737 end
738
738
739 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
739 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
740
740
741 # Renders the TOC with given headings
741 # Renders the TOC with given headings
742 def replace_toc(text, headings)
742 def replace_toc(text, headings)
743 text.gsub!(TOC_RE) do
743 text.gsub!(TOC_RE) do
744 if headings.empty?
744 if headings.empty?
745 ''
745 ''
746 else
746 else
747 div_class = 'toc'
747 div_class = 'toc'
748 div_class << ' right' if $1 == '>'
748 div_class << ' right' if $1 == '>'
749 div_class << ' left' if $1 == '<'
749 div_class << ' left' if $1 == '<'
750 out = "<ul class=\"#{div_class}\"><li>"
750 out = "<ul class=\"#{div_class}\"><li>"
751 root = headings.map(&:first).min
751 root = headings.map(&:first).min
752 current = root
752 current = root
753 started = false
753 started = false
754 headings.each do |level, anchor, item|
754 headings.each do |level, anchor, item|
755 if level > current
755 if level > current
756 out << '<ul><li>' * (level - current)
756 out << '<ul><li>' * (level - current)
757 elsif level < current
757 elsif level < current
758 out << "</li></ul>\n" * (current - level) + "</li><li>"
758 out << "</li></ul>\n" * (current - level) + "</li><li>"
759 elsif started
759 elsif started
760 out << '</li><li>'
760 out << '</li><li>'
761 end
761 end
762 out << "<a href=\"##{anchor}\">#{item}</a>"
762 out << "<a href=\"##{anchor}\">#{item}</a>"
763 current = level
763 current = level
764 started = true
764 started = true
765 end
765 end
766 out << '</li></ul>' * (current - root)
766 out << '</li></ul>' * (current - root)
767 out << '</li></ul>'
767 out << '</li></ul>'
768 end
768 end
769 end
769 end
770 end
770 end
771
771
772 # Same as Rails' simple_format helper without using paragraphs
772 # Same as Rails' simple_format helper without using paragraphs
773 def simple_format_without_paragraph(text)
773 def simple_format_without_paragraph(text)
774 text.to_s.
774 text.to_s.
775 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
775 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
776 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
776 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
777 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
777 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
778 end
778 end
779
779
780 def lang_options_for_select(blank=true)
780 def lang_options_for_select(blank=true)
781 (blank ? [["(auto)", ""]] : []) +
781 (blank ? [["(auto)", ""]] : []) +
782 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
782 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
783 end
783 end
784
784
785 def label_tag_for(name, option_tags = nil, options = {})
785 def label_tag_for(name, option_tags = nil, options = {})
786 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
786 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
787 content_tag("label", label_text)
787 content_tag("label", label_text)
788 end
788 end
789
789
790 def labelled_tabular_form_for(name, object, options, &proc)
790 def labelled_tabular_form_for(name, object, options, &proc)
791 options[:html] ||= {}
791 options[:html] ||= {}
792 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
792 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
793 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
793 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
794 end
794 end
795
795
796 def back_url_hidden_field_tag
796 def back_url_hidden_field_tag
797 back_url = params[:back_url] || request.env['HTTP_REFERER']
797 back_url = params[:back_url] || request.env['HTTP_REFERER']
798 back_url = CGI.unescape(back_url.to_s)
798 back_url = CGI.unescape(back_url.to_s)
799 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
799 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
800 end
800 end
801
801
802 def check_all_links(form_name)
802 def check_all_links(form_name)
803 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
803 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
804 " | ".html_safe +
804 " | ".html_safe +
805 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
805 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
806 end
806 end
807
807
808 def progress_bar(pcts, options={})
808 def progress_bar(pcts, options={})
809 pcts = [pcts, pcts] unless pcts.is_a?(Array)
809 pcts = [pcts, pcts] unless pcts.is_a?(Array)
810 pcts = pcts.collect(&:round)
810 pcts = pcts.collect(&:round)
811 pcts[1] = pcts[1] - pcts[0]
811 pcts[1] = pcts[1] - pcts[0]
812 pcts << (100 - pcts[1] - pcts[0])
812 pcts << (100 - pcts[1] - pcts[0])
813 width = options[:width] || '100px;'
813 width = options[:width] || '100px;'
814 legend = options[:legend] || ''
814 legend = options[:legend] || ''
815 content_tag('table',
815 content_tag('table',
816 content_tag('tr',
816 content_tag('tr',
817 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
817 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0]}%;", :class => 'closed') : ''.html_safe) +
818 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
818 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1]}%;", :class => 'done') : ''.html_safe) +
819 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
819 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2]}%;", :class => 'todo') : ''.html_safe)
820 ), :class => 'progress', :style => "width: #{width};").html_safe +
820 ), :class => 'progress', :style => "width: #{width};").html_safe +
821 content_tag('p', legend, :class => 'pourcent').html_safe
821 content_tag('p', legend, :class => 'pourcent').html_safe
822 end
822 end
823
823
824 def checked_image(checked=true)
824 def checked_image(checked=true)
825 if checked
825 if checked
826 image_tag 'toggle_check.png'
826 image_tag 'toggle_check.png'
827 end
827 end
828 end
828 end
829
829
830 def context_menu(url)
830 def context_menu(url)
831 unless @context_menu_included
831 unless @context_menu_included
832 content_for :header_tags do
832 content_for :header_tags do
833 javascript_include_tag('context_menu') +
833 javascript_include_tag('context_menu') +
834 stylesheet_link_tag('context_menu')
834 stylesheet_link_tag('context_menu')
835 end
835 end
836 if l(:direction) == 'rtl'
836 if l(:direction) == 'rtl'
837 content_for :header_tags do
837 content_for :header_tags do
838 stylesheet_link_tag('context_menu_rtl')
838 stylesheet_link_tag('context_menu_rtl')
839 end
839 end
840 end
840 end
841 @context_menu_included = true
841 @context_menu_included = true
842 end
842 end
843 javascript_tag "new ContextMenu('#{ url_for(url) }')"
843 javascript_tag "new ContextMenu('#{ url_for(url) }')"
844 end
844 end
845
845
846 def context_menu_link(name, url, options={})
846 def context_menu_link(name, url, options={})
847 options[:class] ||= ''
847 options[:class] ||= ''
848 if options.delete(:selected)
848 if options.delete(:selected)
849 options[:class] << ' icon-checked disabled'
849 options[:class] << ' icon-checked disabled'
850 options[:disabled] = true
850 options[:disabled] = true
851 end
851 end
852 if options.delete(:disabled)
852 if options.delete(:disabled)
853 options.delete(:method)
853 options.delete(:method)
854 options.delete(:confirm)
854 options.delete(:confirm)
855 options.delete(:onclick)
855 options.delete(:onclick)
856 options[:class] << ' disabled'
856 options[:class] << ' disabled'
857 url = '#'
857 url = '#'
858 end
858 end
859 link_to h(name), url, options
859 link_to h(name), url, options
860 end
860 end
861
861
862 def calendar_for(field_id)
862 def calendar_for(field_id)
863 include_calendar_headers_tags
863 include_calendar_headers_tags
864 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
864 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
865 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
865 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
866 end
866 end
867
867
868 def include_calendar_headers_tags
868 def include_calendar_headers_tags
869 unless @calendar_headers_tags_included
869 unless @calendar_headers_tags_included
870 @calendar_headers_tags_included = true
870 @calendar_headers_tags_included = true
871 content_for :header_tags do
871 content_for :header_tags do
872 start_of_week = case Setting.start_of_week.to_i
872 start_of_week = case Setting.start_of_week.to_i
873 when 1
873 when 1
874 'Calendar._FD = 1;' # Monday
874 'Calendar._FD = 1;' # Monday
875 when 7
875 when 7
876 'Calendar._FD = 0;' # Sunday
876 'Calendar._FD = 0;' # Sunday
877 when 6
877 when 6
878 'Calendar._FD = 6;' # Saturday
878 'Calendar._FD = 6;' # Saturday
879 else
879 else
880 '' # use language
880 '' # use language
881 end
881 end
882
882
883 javascript_include_tag('calendar/calendar') +
883 javascript_include_tag('calendar/calendar') +
884 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
884 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
885 javascript_tag(start_of_week) +
885 javascript_tag(start_of_week) +
886 javascript_include_tag('calendar/calendar-setup') +
886 javascript_include_tag('calendar/calendar-setup') +
887 stylesheet_link_tag('calendar')
887 stylesheet_link_tag('calendar')
888 end
888 end
889 end
889 end
890 end
890 end
891
891
892 def content_for(name, content = nil, &block)
892 def content_for(name, content = nil, &block)
893 @has_content ||= {}
893 @has_content ||= {}
894 @has_content[name] = true
894 @has_content[name] = true
895 super(name, content, &block)
895 super(name, content, &block)
896 end
896 end
897
897
898 def has_content?(name)
898 def has_content?(name)
899 (@has_content && @has_content[name]) || false
899 (@has_content && @has_content[name]) || false
900 end
900 end
901
901
902 def email_delivery_enabled?
902 def email_delivery_enabled?
903 !!ActionMailer::Base.perform_deliveries
903 !!ActionMailer::Base.perform_deliveries
904 end
904 end
905
905
906 # Returns the avatar image tag for the given +user+ if avatars are enabled
906 # Returns the avatar image tag for the given +user+ if avatars are enabled
907 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
907 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
908 def avatar(user, options = { })
908 def avatar(user, options = { })
909 if Setting.gravatar_enabled?
909 if Setting.gravatar_enabled?
910 options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
910 options.merge!({:ssl => (defined?(request) && request.ssl?), :default => Setting.gravatar_default})
911 email = nil
911 email = nil
912 if user.respond_to?(:mail)
912 if user.respond_to?(:mail)
913 email = user.mail
913 email = user.mail
914 elsif user.to_s =~ %r{<(.+?)>}
914 elsif user.to_s =~ %r{<(.+?)>}
915 email = $1
915 email = $1
916 end
916 end
917 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
917 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
918 else
918 else
919 ''
919 ''
920 end
920 end
921 end
921 end
922
922
923 def sanitize_anchor_name(anchor)
923 def sanitize_anchor_name(anchor)
924 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
924 anchor.gsub(%r{[^\w\s\-]}, '').gsub(%r{\s+(\-+\s*)?}, '-')
925 end
925 end
926
926
927 # Returns the javascript tags that are included in the html layout head
927 # Returns the javascript tags that are included in the html layout head
928 def javascript_heads
928 def javascript_heads
929 tags = javascript_include_tag(:defaults)
929 tags = javascript_include_tag(:defaults)
930 unless User.current.pref.warn_on_leaving_unsaved == '0'
930 unless User.current.pref.warn_on_leaving_unsaved == '0'
931 tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
931 tags << "\n".html_safe + javascript_tag("Event.observe(window, 'load', function(){ new WarnLeavingUnsaved('#{escape_javascript( l(:text_warn_on_leaving_unsaved) )}'); });")
932 end
932 end
933 tags
933 tags
934 end
934 end
935
935
936 def favicon
936 def favicon
937 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
937 "<link rel='shortcut icon' href='#{image_path('/favicon.ico')}' />".html_safe
938 end
938 end
939
939
940 def robot_exclusion_tag
940 def robot_exclusion_tag
941 '<meta name="robots" content="noindex,follow,noarchive" />'
941 '<meta name="robots" content="noindex,follow,noarchive" />'
942 end
942 end
943
943
944 # Returns true if arg is expected in the API response
944 # Returns true if arg is expected in the API response
945 def include_in_api_response?(arg)
945 def include_in_api_response?(arg)
946 unless @included_in_api_response
946 unless @included_in_api_response
947 param = params[:include]
947 param = params[:include]
948 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
948 @included_in_api_response = param.is_a?(Array) ? param.collect(&:to_s) : param.to_s.split(',')
949 @included_in_api_response.collect!(&:strip)
949 @included_in_api_response.collect!(&:strip)
950 end
950 end
951 @included_in_api_response.include?(arg.to_s)
951 @included_in_api_response.include?(arg.to_s)
952 end
952 end
953
953
954 # Returns options or nil if nometa param or X-Redmine-Nometa header
954 # Returns options or nil if nometa param or X-Redmine-Nometa header
955 # was set in the request
955 # was set in the request
956 def api_meta(options)
956 def api_meta(options)
957 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
957 if params[:nometa].present? || request.headers['X-Redmine-Nometa']
958 # compatibility mode for activeresource clients that raise
958 # compatibility mode for activeresource clients that raise
959 # an error when unserializing an array with attributes
959 # an error when unserializing an array with attributes
960 nil
960 nil
961 else
961 else
962 options
962 options
963 end
963 end
964 end
964 end
965
965
966 private
966 private
967
967
968 def wiki_helper
968 def wiki_helper
969 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
969 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
970 extend helper
970 extend helper
971 return self
971 return self
972 end
972 end
973
973
974 def link_to_content_update(text, url_params = {}, html_options = {})
974 def link_to_content_update(text, url_params = {}, html_options = {})
975 link_to(text, url_params, html_options)
975 link_to(text, url_params, html_options)
976 end
976 end
977 end
977 end
General Comments 0
You need to be logged in to leave comments. Login now