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