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